Index: Core/LoggingEvent.cs
===================================================================
--- Core/LoggingEvent.cs	(revision 1581269)
+++ 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,7 +1279,6 @@
 
 			// event properties
 			PropertiesDictionary eventProperties = new PropertiesDictionary();
-			eventProperties[UserNameProperty] = UserName;
 			eventProperties[IdentityProperty] = Identity;
 			m_compositeProperties.Add(eventProperties);
 
Index: Util/CompositeProperties.cs
===================================================================
--- Util/CompositeProperties.cs	(revision 1581269)
+++ Util/CompositeProperties.cs	(working copy)
@@ -36,6 +36,7 @@
 	{
 		#region Private Instance Fields
 
+		private string m_currentUserName = null;
 		private PropertiesDictionary m_flattened = null;
 		private ArrayList m_nestedProperties = new ArrayList();
 
@@ -82,6 +83,17 @@
 		{
 			get 
 			{
+				// treat the UserName property specially
+				if (key == log4net.Core.LoggingEvent.UserNameProperty)
+				{
+					if (m_currentUserName == null)
+					{
+						// get the current user name and cache it
+						m_currentUserName = SystemInfo.GetCurrentUserName();
+					}
+					return m_currentUserName;
+				}
+
 				// Look in the flattened properties first
 				if (m_flattened != null)
 				{
Index: Util/SystemInfo.cs
===================================================================
--- Util/SystemInfo.cs	(revision 1581269)
+++ 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);
 		}
 
+		/// <summary>
+		/// Gets the name of the current user.
+		/// </summary>
+		/// <returns>
+		/// The name of the current user, or <c>NOT AVAILABLE</c> when the 
+		/// underlying runtime has no support for retrieving the name of the 
+		/// current user.
+		/// </returns>
+		/// <remarks>
+		/// <para>
+		/// Calls <c>WindowsIdentity.GetCurrent().Name</c> to get the name of
+		/// the current windows user.
+		/// </para>
+		/// <para>
+		/// 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.
+		/// </para>
+		/// <para>
+		/// However, the <c>WindowsIdentity.GetCurrent()</c> call seems to 
+		/// return different objects every time, so the current implementation 
+		/// doesn't do this type of caching.
+		/// </para>
+		/// <para>
+		/// Timing for these operations:
+		/// </para>
+		/// <list type="table">
+		///   <listheader>
+		///     <term>Method</term>
+		///     <description>Results</description>
+		///   </listheader>
+		///   <item>
+		///	    <term><c>WindowsIdentity.GetCurrent()</c></term>
+		///	    <description>10000 loops, 00:00:00.2031250 seconds</description>
+		///   </item>
+		///   <item>
+		///	    <term><c>WindowsIdentity.GetCurrent().Name</c></term>
+		///	    <description>10000 loops, 00:00:08.0468750 seconds</description>
+		///   </item>
+		/// </list>
+		/// <para>
+		/// This means we could speed things up almost 40 times by caching the 
+		/// value of the <c>WindowsIdentity.GetCurrent().Name</c> property, since 
+		/// this takes (8.04-0.20) = 7.84375 seconds.
+		/// </para>
+		/// </remarks>
+		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
+		}
+
+		/// <summary>
+		/// Gets the identity of the current thread principal.
+		/// </summary>
+		/// <returns>
+		/// The string name of the identity of the current thread principal.
+		/// </returns>
+		/// <remarks>
+		/// <para>
+		/// Calls <c>System.Threading.Thread.CurrentPrincipal.Identity.Name</c> to get
+		/// the name of the current thread principal.
+		/// </para>
+		/// </remarks>
+		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
