Index: src/log4net/Core/LoggingEvent.cs =================================================================== --- src/log4net/Core/LoggingEvent.cs (revision 1599086) +++ src/log4net/Core/LoggingEvent.cs (working copy) @@ -828,31 +828,7 @@ { if (m_data.UserName == null && this.m_cacheUpdatable) { -#if NETCF - // On compact framework there's no notion of current Windows user - m_data.UserName = SystemInfo.NotAvailableText; -#else - try - { - WindowsIdentity windowsIdentity = WindowsIdentity.GetCurrent(); - if (windowsIdentity != null && windowsIdentity.Name != null) - { - m_data.UserName = windowsIdentity.Name; - } - else - { - m_data.UserName = ""; - } - } - catch(System.Security.SecurityException) - { - // This security exception will occur if the caller does not have - // some undefined set of SecurityPermission flags. - LogLog.Debug(declaringType, "Security exception while trying to get current windows identity. Error Ignored. Empty user name."); - - m_data.UserName = ""; - } -#endif + m_data.UserName = SystemInfo.GetCurrentUserName(); } return m_data.UserName; } @@ -876,41 +852,7 @@ { if (m_data.Identity == null && this.m_cacheUpdatable) { -#if NETCF - // On compact framework there's no notion of current thread principals - m_data.Identity = SystemInfo.NotAvailableText; -#else - try - { - if (System.Threading.Thread.CurrentPrincipal != null && - System.Threading.Thread.CurrentPrincipal.Identity != null && - System.Threading.Thread.CurrentPrincipal.Identity.Name != null) - { - m_data.Identity = System.Threading.Thread.CurrentPrincipal.Identity.Name; - } - else - { - m_data.Identity = ""; - } - } - catch (ObjectDisposedException) - { - // This exception will occur if System.Threading.Thread.CurrentPrincipal.Identity is not null but - // the getter of the property Name tries to access disposed objects. - // Seen to happen on IIS 7 or greater with windows authentication. - LogLog.Debug(declaringType, "Object disposed exception while trying to get current thread principal. Error Ignored. Empty identity name."); - - m_data.Identity = ""; - } - catch (System.Security.SecurityException) - { - // This security exception will occur if the caller does not have - // some undefined set of SecurityPermission flags. - LogLog.Debug(declaringType, "Security exception while trying to get current thread principal. Error Ignored. Empty identity name."); - - m_data.Identity = ""; - } -#endif + m_data.Identity = SystemInfo.GetCurrentIdentity(); } return m_data.Identity; } @@ -1337,9 +1279,11 @@ // event properties PropertiesDictionary eventProperties = new PropertiesDictionary(); - eventProperties[UserNameProperty] = UserName; eventProperties[IdentityProperty] = Identity; m_compositeProperties.Add(eventProperties); + System.Collections.Generic.Dictionary dict = new System.Collections.Generic.Dictionary(); + dict[UserNameProperty] = SystemInfo.GetCurrentUserName; + m_compositeProperties.Add(new LazyReadOnlyPropertiesDictionary(dict)); m_compositeProperties.Add(GlobalContext.Properties.GetReadOnlyProperties()); } Index: src/log4net/Util/LazyReadOnlyPropertiesDictionary.cs =================================================================== --- src/log4net/Util/LazyReadOnlyPropertiesDictionary.cs (revision 0) +++ src/log4net/Util/LazyReadOnlyPropertiesDictionary.cs (working copy) @@ -0,0 +1,161 @@ +#region Apache License +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to you under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +using System; +using System.Collections.Generic; +#if !NETCF +using System.Runtime.Serialization; +#endif + +namespace log4net.Util +{ + + /// + /// Delegate used to produce values for the lazy dictionary. + /// + public delegate object Producer(); + + /// + /// String keyed map that is read only and lazily produces its values. + /// + /// + /// + /// This collection is readonly and cannot be modified. + /// + /// + /// While this collection is serializable only member + /// objects that are serializable will + /// be serialized along with this collection. + /// + /// + /// Nicko Cadell + /// Gert Driesen + public class LazyReadOnlyPropertiesDictionary : ReadOnlyPropertiesDictionary + { + #region Private Instance Fields + + /// + /// The Hashtable used to store the producers + /// +#if !NETCF + [NonSerialized] +#endif + private readonly IDictionary producers; + + #endregion Private Instance Fields + + #region Public Instance Constructors + + /// + /// Constructor + /// + /// + /// + /// Initializes a new instance of the class. + /// + /// + public LazyReadOnlyPropertiesDictionary(IDictionary producers) + { + this.producers = new Dictionary(producers); + } + + #endregion Public Instance Constructors + + #region Private Instance Constructors + +#if !NETCF + /// + /// Deserialization constructor + /// + protected LazyReadOnlyPropertiesDictionary(SerializationInfo info, StreamingContext context) + : base(info, context) + { + this.producers = new Dictionary(); + } +#endif + + #endregion Protected Instance Constructors + + #region Public Instance Properties + + /// + /// Gets the key names. + /// + /// An array of all the keys. + /// + /// + /// Gets the key names. + /// + /// + public override string[] GetKeys() + { + string[] keys = new String[producers.Count]; + producers.Keys.CopyTo(keys, 0); + return keys; + } + + /// + /// Gets or sets the value of the property with the specified key. + /// + /// + /// The value of the property with the specified key. + /// + /// The key of the property to get or set. + /// + /// + /// The property value will only be serialized if it is serializable. + /// If it cannot be serialized it will be silently ignored if + /// a serialization operation is performed. + /// + /// + public override object this[string key] + { + get { + if (base.Contains(key) || !Contains(key)) + { + return base[key]; + } + object produced = producers[key](); + InnerHashtable[key] = produced; + return produced; + } + } + + #endregion Public Instance Properties + + #region Public Instance Methods + + /// + /// Test if the dictionary contains a specified key + /// + /// the key to look for + /// true if the dictionary contains the specified key + /// + /// + /// Test if the dictionary contains a specified key + /// + /// + public override bool Contains(string key) + { + return base.Contains(key) || producers.ContainsKey(key); + } + + #endregion + } +} \ No newline at end of file Index: src/log4net/Util/ReadOnlyPropertiesDictionary.cs =================================================================== --- src/log4net/Util/ReadOnlyPropertiesDictionary.cs (revision 1599086) +++ src/log4net/Util/ReadOnlyPropertiesDictionary.cs (working copy) @@ -126,7 +126,7 @@ /// Gets the key names. /// /// - public string[] GetKeys() + public virtual string[] GetKeys() { string[] keys = new String[InnerHashtable.Count]; InnerHashtable.Keys.CopyTo(keys, 0); @@ -167,7 +167,7 @@ /// Test if the dictionary contains a specified key /// /// - public bool Contains(string key) + public virtual bool Contains(string key) { return InnerHashtable.Contains(key); } Index: src/log4net/Util/SystemInfo.cs =================================================================== --- src/log4net/Util/SystemInfo.cs (revision 1599086) +++ src/log4net/Util/SystemInfo.cs (working copy) @@ -24,6 +24,7 @@ using System.IO; using System.Runtime.InteropServices; using System.Collections; +using System.Security.Principal; namespace log4net.Util { @@ -1002,6 +1003,133 @@ return new Hashtable(StringComparer.OrdinalIgnoreCase); } + /// + /// Gets the name of the current user. + /// + /// + /// The name of the current user, or NOT AVAILABLE when the + /// underlying runtime has no support for retrieving the name of the + /// current user. + /// + /// + /// + /// Calls WindowsIdentity.GetCurrent().Name to get the name of + /// the current windows user. + /// + /// + /// To improve performance, we could cache the string representation of + /// the name, and reuse that as long as the identity stayed constant. + /// Once the identity changed, we would need to re-assign and re-render + /// the string. + /// + /// + /// However, the WindowsIdentity.GetCurrent() call seems to + /// return different objects every time, so the current implementation + /// doesn't do this type of caching. + /// + /// + /// Timing for these operations: + /// + /// + /// + /// Method + /// Results + /// + /// + /// WindowsIdentity.GetCurrent() + /// 10000 loops, 00:00:00.2031250 seconds + /// + /// + /// WindowsIdentity.GetCurrent().Name + /// 10000 loops, 00:00:08.0468750 seconds + /// + /// + /// + /// This means we could speed things up almost 40 times by caching the + /// value of the WindowsIdentity.GetCurrent().Name property, since + /// this takes (8.04-0.20) = 7.84375 seconds. + /// + /// + public static string GetCurrentUserName() + { +#if NETCF + // On compact framework there's no notion of current Windows user + return NotAvailableText; +#else + try + { + WindowsIdentity windowsIdentity = WindowsIdentity.GetCurrent(); + if (windowsIdentity != null && windowsIdentity.Name != null) + { + return windowsIdentity.Name; + } + else + { + return ""; + } + } + catch (System.Security.SecurityException) + { + // This security exception will occur if the caller does not have + // some undefined set of SecurityPermission flags. + LogLog.Debug(declaringType, "Security exception while trying to get current windows identity. Error Ignored. Empty user name."); + + return ""; + } +#endif + } + + /// + /// Gets the identity of the current thread principal. + /// + /// + /// The string name of the identity of the current thread principal. + /// + /// + /// + /// Calls System.Threading.Thread.CurrentPrincipal.Identity.Name to get + /// the name of the current thread principal. + /// + /// + public static string GetCurrentIdentity() + { +#if NETCF + // On compact framework there's no notion of current thread principals + return SystemInfo.NotAvailableText; +#else + try + { + if (System.Threading.Thread.CurrentPrincipal != null && + System.Threading.Thread.CurrentPrincipal.Identity != null && + System.Threading.Thread.CurrentPrincipal.Identity.Name != null) + { + return System.Threading.Thread.CurrentPrincipal.Identity.Name; + } + else + { + return ""; + } + } + catch (ObjectDisposedException) + { + // This exception will occur if System.Threading.Thread.CurrentPrincipal.Identity is not null but + // the getter of the property Name tries to access disposed objects. + // Seen to happen on IIS 7 or greater with windows authentication. + LogLog.Debug(declaringType, "Object disposed exception while trying to get current thread principal. Error Ignored. Empty identity name."); + + return ""; + } + catch (System.Security.SecurityException) + { + // This security exception will occur if the caller does not have + // some undefined set of SecurityPermission flags. + LogLog.Debug(declaringType, "Security exception while trying to get current thread principal. Error Ignored. Empty identity name."); + + return ""; + } +#endif + } + #endregion Public Static Methods #region Private Static Methods