You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4net-dev@logging.apache.org by ni...@apache.org on 2004/09/09 23:53:14 UTC

cvs commit: logging-log4net/src/Util/PatternStringConverters PropertyPatternConverter.cs

nicko       2004/09/09 14:53:14

  Modified:    src      MDC.cs NDC.cs
               src/Appender EventLogAppender.cs RemotingAppender.cs
                        SmtpAppender.cs SmtpPickupDirAppender.cs
                        UdpAppender.cs
               src/Core LoggingEvent.cs
               src/Filter MdcFilter.cs NdcFilter.cs
               src/Layout PatternLayout.cs RawPropertyLayout.cs
                        XMLLayout.cs XmlLayoutSchemaLog4j.cs
               src/Layout/Pattern NdcPatternConverter.cs
                        PropertyPatternConverter.cs
               src/Util PatternString.cs
  Added:       src      ThreadContext.cs
               src/Core IFixingRequired.cs
               src/Filter PropertyFilter.cs
               src/Util CompositeProperties.cs ThreadContextList.cs
                        ThreadContextLists.cs ThreadContextProperties.cs
                        ThreadContextStack.cs ThreadContextStacks.cs
               src/Util/PatternStringConverters PropertyPatternConverter.cs
  Removed:     src/Layout/Pattern GlobalPropertyPatternConverter.cs
                        MdcPatternConverter.cs
  Log:
  Added ThreadContext.
  The ThreadContext.Properties is a replacement for the MDC. The ThreadContext.Stacks is a replacement for the NDC.
  The ThreadContext.Stacks and ThreadContext.Lists are stored in the ThreadContext.Properties.
  The MDC and NDC forward to the new implementation where possible.
  The contexts are composed into layers; GlobalContext.Properties are overridden by ThreadContext.Properties and they are overridden by LoggingEvent.EventProperties.
  Changed filters and layouts to lookup properties from the LoggingEvent.
  
  Revision  Changes    Path
  1.4       +16 -71    logging-log4net/src/MDC.cs
  
  Index: MDC.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/MDC.cs,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- MDC.cs	30 May 2004 11:30:34 -0000	1.3
  +++ MDC.cs	9 Sep 2004 21:53:13 -0000	1.4
  @@ -26,6 +26,10 @@
   	/// </summary>
   	/// <remarks>
   	/// <para>
  +	/// The MDC is deprecated and has been replaced by the <see cref="ThreadContext.Properties"/>.
  +	/// The current MDC implementation forwards to the ThreadContext.Properties.
  +	/// </para>
  +	/// <para>
   	/// The MDC class is similar to the <see cref="NDC"/> class except that it is
   	/// based on a map instead of a stack. It provides <i>mapped
   	/// diagnostic contexts</i>. A <i>Mapped Diagnostic Context</i>, or
  @@ -39,6 +43,7 @@
   	/// </remarks>
   	/// <author>Nicko Cadell</author>
   	/// <author>Gert Driesen</author>
  +	/*[Obsolete("MDC has been replaced by ThreadContext.Properties")]*/
   	public sealed class MDC
   	{
   		#region Private Instance Constructors
  @@ -68,13 +73,15 @@
   		/// </remarks>
   		/// <param name="key">The key to lookup in the MDC.</param>
   		/// <returns>The string value held for the key, or a <c>null</c> reference if no corresponding value is found.</returns>
  +		/*[Obsolete("MDC has been replaced by ThreadContext.Properties")]*/
   		public static string Get(string key)
   		{
  -			if (key == null)
  +			object obj = ThreadContext.Properties[key];
  +			if (obj == null)
   			{
  -				throw new ArgumentNullException("key");
  +				return null;
   			}
  -			return GetMap()[key] as string;
  +			return obj.ToString();
   		}
   
   		/// <summary>
  @@ -94,21 +101,10 @@
   		/// </remarks>
   		/// <param name="key">The key to store the value under.</param>
   		/// <param name="value">The value to store.</param>
  +		/*[Obsolete("MDC has been replaced by ThreadContext.Properties")]*/
   		public static void Set(string key, string value)
   		{
  -			if (key == null)
  -			{
  -				throw new ArgumentNullException("key");
  -			}
  -
  -			if (value == null)
  -			{
  -				GetMap().Remove(key);
  -			}
  -			else
  -			{
  -				GetMap()[key] = value;
  -			}
  +			ThreadContext.Properties[key] = value;
   		}
   
   		/// <summary>
  @@ -120,14 +116,10 @@
   		/// </para>
   		/// </remarks>
   		/// <param name="key">The key to remove.</param>
  +		/*[Obsolete("MDC has been replaced by ThreadContext.Properties")]*/
   		public static void Remove(string key)
   		{
  -			if (key == null)
  -			{
  -				throw new ArgumentNullException("key");
  -			}
  -
  -			Set(key, null);
  +			ThreadContext.Properties.Remove(key);
   		}
   
   		/// <summary>
  @@ -138,59 +130,12 @@
   		/// Remove all the entries from this thread's MDC
   		/// </para>
   		/// </remarks>
  +		/*[Obsolete("MDC has been replaced by ThreadContext.Properties")]*/
   		public static void Clear()
   		{
  -			Hashtable map = (Hashtable)System.Threading.Thread.GetData(s_slot);
  -			if (map != null)
  -			{
  -				map.Clear();
  -			}
  +			ThreadContext.Properties.Clear();
   		}
   
   		#endregion Public Static Methods
  -
  -		#region Internal Static Methods
  -
  -		/// <summary>
  -		/// Gets the map on this thread.
  -		/// </summary>
  -		/// <returns>The map on the current thread.</returns>
  -		internal static IDictionary GetMap()
  -		{
  -			Hashtable map = (Hashtable)System.Threading.Thread.GetData(s_slot);
  -			if (map == null)
  -			{
  -				map = new Hashtable();
  -				System.Threading.Thread.SetData(s_slot, map);
  -			}
  -			return map;
  -		}
  -
  -		/// <summary>
  -		/// Gets a readonly copy of the map on this thread.
  -		/// </summary>
  -		/// <returns>A readonly copy of the map on the current thread.</returns>
  -		internal static IDictionary CopyMap()
  -		{
  -			Hashtable map = (Hashtable)System.Threading.Thread.GetData(s_slot);
  -			if (map == null)
  -			{
  -				return log4net.Util.EmptyDictionary.Instance;
  -			}
  -
  -			// Return a copy of the map
  -			return (IDictionary)map.Clone();
  -		}
  -
  -		#endregion Internal Static Methods
  -
  -		#region Private Static Fields
  -
  -		/// <summary>
  -		/// The thread local data slot to use for context information.
  -		/// </summary>
  -		private readonly static LocalDataStoreSlot s_slot = System.Threading.Thread.AllocateDataSlot();
  -
  -		#endregion Private Static Fields
   	}
   }
  
  
  
  1.7       +20 -233   logging-log4net/src/NDC.cs
  
  Index: NDC.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/NDC.cs,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- NDC.cs	7 Jun 2004 01:09:08 -0000	1.6
  +++ NDC.cs	9 Sep 2004 21:53:13 -0000	1.7
  @@ -29,6 +29,10 @@
   	/// </summary>
   	/// <remarks>
   	/// <para>
  +	/// The NDC is deprecated and has been replaced by the <see cref="ThreadContext.Stacks"/>.
  +	/// The current NDC implementation forwards to the ThreadContext.Stacks["NDC"].
  +	/// </para>
  +	/// <para>
   	/// A Nested Diagnostic Context, or NDC in short, is an instrument
   	/// to distinguish interleaved log output from different sources. Log
   	/// output is typically interleaved when a server handles multiple
  @@ -56,6 +60,7 @@
   	/// </example>
   	/// <author>Nicko Cadell</author>
   	/// <author>Gert Driesen</author>
  +	/*[Obsolete("NDC has been replaced by ThreadContext.Stacks")]*/
   	public sealed class NDC
   	{
   		#region Private Instance Constructors
  @@ -88,9 +93,10 @@
   		/// </para>
   		/// </remarks>
   		/// <seealso cref="SetMaxDepth"/>
  +		[Obsolete("NDC has been replaced by ThreadContext.Stacks", true)]
   		public static int Depth
   		{
  -			get { return GetStack().Count; }
  +			get { throw new NotSupportedException("NDC has been replaced by ThreadContext.Stacks"); }
   		}
   
   		#endregion Public Static Properties
  @@ -104,9 +110,10 @@
   		/// <remarks>
   		/// After calling this method the <see cref="Depth"/> will be <c>0</c>.
   		/// </remarks>
  +		[Obsolete("NDC has been replaced by ThreadContext.Stacks", true)]
   		public static void Clear() 
   		{
  -			GetStack().Clear();
  +			throw new NotSupportedException("NDC has been replaced by ThreadContext.Stacks");
   		}
   
   		/// <summary>
  @@ -118,9 +125,10 @@
   		/// parent thread.
   		/// </remarks>
   		/// <returns>A clone of the context info for this thread.</returns>
  +		[Obsolete("NDC has been replaced by ThreadContext.Stacks", true)]
   		public static Stack CloneStack() 
   		{
  -			return (Stack)GetStack().Clone();
  +			throw new NotSupportedException("NDC has been replaced by ThreadContext.Stacks");
   		}
   
   		/// <summary>
  @@ -136,14 +144,10 @@
   		/// this method.
   		/// </remarks>
   		/// <param name="stack">The context stack to inherit.</param>
  +		[Obsolete("NDC has been replaced by ThreadContext.Stacks", true)]
   		public static void Inherit(Stack stack) 
   		{
  -			if (stack == null)
  -			{
  -				throw new ArgumentNullException("stack");
  -			}
  -
  -			System.Threading.Thread.SetData(s_slot, stack);
  +			throw new NotSupportedException("NDC has been replaced by ThreadContext.Stacks");
   		}
   
   		/// <summary>
  @@ -158,14 +162,10 @@
   		/// The message in the context that was removed from the top 
   		/// of the stack.
   		/// </returns>
  +		/*[Obsolete("NDC has been replaced by ThreadContext.Stacks")]*/
   		public static string Pop() 
   		{
  -			Stack stack = GetStack();
  -			if (stack.Count > 0)
  -			{
  -				return ((DiagnosticContext)(stack.Pop())).Message;
  -			}
  -			return "";
  +			return ThreadContext.Stacks["NDC"].Pop();
   		}
   
   		/// <summary>
  @@ -190,12 +190,10 @@
   		///	}
   		/// </code>
   		/// </example>
  +		/*[Obsolete("NDC has been replaced by ThreadContext.Stacks")]*/
   		public static IDisposable Push(string message) 
   		{
  -			Stack stack = GetStack();
  -			stack.Push(new DiagnosticContext(message, (stack.Count>0) ? (DiagnosticContext)stack.Peek() : null));
  -
  -			return new NDCAutoDisposeFrame(stack, stack.Count - 1);
  +			return ThreadContext.Stacks["NDC"].Push(message);
   		}
   
   		/// <summary>
  @@ -205,6 +203,7 @@
   		/// <remarks>
   		/// This method is not implemented.
   		/// </remarks>
  +		[Obsolete("NDC has been replaced by ThreadContext.Stacks")]
   		public static void Remove() 
   		{
   		}
  @@ -220,224 +219,12 @@
   		/// call. This can be used to return to a known context depth.
   		/// </remarks>
   		/// <param name="maxDepth">The maximum depth of the stack</param>
  +		[Obsolete("NDC has been replaced by ThreadContext.Stacks", true)]
   		static public void SetMaxDepth(int maxDepth) 
   		{
  -			if (maxDepth < 0)
  -			{
  -				throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("maxDepth", (object)maxDepth, "Parameter: maxDepth, Value: ["+maxDepth+"] out of range. Nonnegative number required");
  -			}
  -
  -			Stack stack = GetStack();
  -			while(stack.Count > maxDepth)
  -			{
  -				stack.Pop();
  -			}
  +			throw new NotSupportedException("NDC has been replaced by ThreadContext.Stacks");
   		}
   
   		#endregion Public Static Methods
  -
  -		#region Internal Static Methods
  -
  -		/// <summary>
  -		/// Gets the current context information.
  -		/// </summary>
  -		/// <returns>The current context information.</returns>
  -		internal static string Get() 
  -		{
  -			Stack stack = GetStack();
  -			if (stack.Count > 0)
  -			{
  -				return ((DiagnosticContext)(stack.Peek())).FullMessage;
  -			}
  -			return null;
  -		}
  -  
  -		#endregion Internal Static Methods
  -
  -		#region Private Static Methods
  -
  -		/// <summary>
  -		/// Gets the stack of context objects on this thread.
  -		/// </summary>
  -		/// <returns>The stack of context objects on the current thread.</returns>
  -		static private Stack GetStack()
  -		{
  -			Stack stack = (Stack)System.Threading.Thread.GetData(s_slot);
  -			if (stack == null)
  -			{
  -				stack = new Stack();
  -				System.Threading.Thread.SetData(s_slot, stack);
  -			}
  -			return stack;
  -		}
  -
  -		#endregion Private Static Methods
  -
  -		#region Private Static Fields
  -
  -		/// <summary>
  -		/// The thread local data slot to use for context information.
  -		/// </summary>
  -		private readonly static LocalDataStoreSlot s_slot = System.Threading.Thread.AllocateDataSlot();
  -
  -		#endregion Private Static Fields
  -
  -		/// <summary>
  -		/// Inner class used to represent a single context in the stack.
  -		/// </summary>
  -		internal class DiagnosticContext 
  -		{
  -			#region Internal Instance Constructors
  -
  -			/// <summary>
  -			/// Initializes a new instance of the <see cref="DiagnosticContext" /> class
  -			/// with the specified message and parent context.
  -			/// </summary>
  -			/// <param name="message">The message for this context.</param>
  -			/// <param name="parent">The parent context in the chain.</param>
  -			internal DiagnosticContext(string message, DiagnosticContext parent) 
  -			{
  -				m_message = message;
  -				if (parent != null) 
  -				{
  -					m_fullMessage = parent.FullMessage + ' ' + message;
  -				} 
  -				else 
  -				{
  -					m_fullMessage = message;
  -				}
  -			}
  -
  -			#endregion Internal Instance Constructors
  -
  -			#region Internal Instance Properties
  -
  -			/// <summary>
  -			/// Get the message.
  -			/// </summary>
  -			/// <value>The message.</value>
  -			internal string Message
  -			{
  -				get { return m_message; }
  -			}
  -
  -			/// <summary>
  -			/// Gets the full text of the context down to the root level.
  -			/// </summary>
  -			/// <value>
  -			/// The full text of the context down to the root level.
  -			/// </value>
  -			internal string FullMessage
  -			{
  -				get { return m_fullMessage; }
  -			}
  -
  -			#endregion Internal Instance Properties
  -
  -			#region Private Instance Fields
  -
  -			private string m_fullMessage;
  -			private string m_message;
  -    
  -			#endregion
  -		}
  -
  -		/// <summary>
  -		/// Inner class that is returned from <see cref="NDC.Push"/>
  -		/// </summary>
  -		/// <remarks>
  - 		/// This class is disposable and when it is disposed it automatically
  - 		/// returns the NDC to the correct depth.
  -		/// </remarks>
  -		internal class NDCAutoDisposeFrame : IDisposable
  -		{
  -			#region Internal Instance Constructors
  -
  -			/// <summary>
  -			/// Initializes a new instance of the <see cref="NDCAutoDisposeFrame" /> class with
  -			/// the specified stack and return depth.
  -			/// </summary>
  -			/// <param name="frameStack">The internal stack used by the NDC.</param>
  -			/// <param name="frameDepth">The depth to return the stack to when this object is disposed.</param>
  -			internal NDCAutoDisposeFrame(Stack frameStack, int frameDepth)
  -			{
  -				m_frameStack = frameStack;
  -				m_frameDepth = frameDepth;
  -			}
  -
  -			#endregion Internal Instance Constructors
  -
  -			#region Implementation of IDisposable
  -
  -			/// <summary>
  -			/// Returns the NDC stack to the correct depth.
  -			/// </summary>
  -			public void Dispose()
  -			{
  -				if (m_frameDepth >= 0 && m_frameStack != null)
  -				{
  -					while(m_frameStack.Count > m_frameDepth)
  -					{
  -						m_frameStack.Pop();
  -					}
  -				}
  -			}
  -
  -			#endregion Implementation of IDisposable
  -
  -			#region Private Instance Fields
  -
  -			/// <summary>
  -			/// The NDC internal stack
  -			/// </summary>
  -			private Stack m_frameStack;
  -
  -			/// <summary>
  -			/// The depth to rethrow the stack to when this instance is disposed
  -			/// </summary>
  -			private int m_frameDepth;
  -
  -			#endregion Private Instance Fields
  -		}
  -
  -#if NETCF
  -		/// <summary>
  -		/// Subclass of <see cref="System.Collections.Stack"/> to
  -		/// provide missing methods.
  -		/// </summary>
  -		/// <remarks>
  -		/// The Compact Framework version of the <see cref="System.Collections.Stack"/>
  -		/// class is missing the <c>Clear</c> and <c>Clone</c> methods.
  -		/// This subclass adds implementations of those missing methods.
  -		/// </remarks>
  -		public class Stack : System.Collections.Stack
  -		{
  -			/// <summary>
  -			/// Clears the stack of all elements.
  -			/// </summary>
  -			public void Clear()
  -			{
  -				while(Count > 0)
  -				{
  -					Pop();
  -				}
  -			}
  -
  -			/// <summary>
  -			/// Makes a shallow copy of the stack's elements.
  -			/// </summary>
  -			/// <returns>A new stack that has a shallow copy of the stack's elements.</returns>
  -			public Stack Clone()
  -			{
  -				Stack res = new Stack();
  -				object[] items = ToArray();
  -				foreach(object item in items)
  -				{
  -					res.Push(item);
  -				}
  -				return res;
  -			}
  -		}
  -#endif
   	}
   }
  
  
  
  1.1                  logging-log4net/src/ThreadContext.cs
  
  Index: ThreadContext.cs
  ===================================================================
  #region Copyright & License
  //
  // Copyright 2001-2004 The Apache Software Foundation
  //
  // Licensed 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;
  
  using log4net.Util;
  
  namespace log4net
  {
  	/// <summary>
  	/// The log4net Thread Context.
  	/// </summary>
  	/// <remarks>
  	/// <para>
  	/// The <c>ThreadContext</c> provides a location for thread specific debugging 
  	/// information to be stored.
  	/// </para>
  	/// <para>
  	/// The thread context has a properties map and a stack.
  	/// The properties and stack can 
  	/// be included in the output of log messages. The <see cref="log4net.Layout.PatternLayout"/>
  	/// supports selecting and outputting these properties.
  	/// </para>
  	/// <para>
  	/// The Thread Context provides a diagnostic context for the current thread. 
  	/// This is an instrument for distinguishing interleaved log
  	/// output from different sources. Log output is typically interleaved
  	/// when a server handles multiple clients near-simultaneously.
  	/// </para>
  	/// <para>
  	/// The Thread Context is managed on a per thread basis.
  	/// </para>
  	/// </remarks>
  	/// <example>Example of using the thread context properties to store a username.
  	/// <code>
  	/// ThreadContext.Properties["user"] = userName;
  	///	log.Info("This log message has a ThreadContext Property called 'user'");
  	/// </code>
  	/// </example>
  	/// <example>Example of how to push a message into the context stack
  	/// <code>
  	///	using(ThreadContext.Stack.Push("my context message"))
  	///	{
  	///		log.Info("This log message has a ThreadContext Stack message that includes 'my context message'");
  	///	
  	///	} // at the end of the using block the message is automatically popped 
  	/// </code>
  	/// </example>
  	/// <author>Nicko Cadell</author>
  	public sealed class ThreadContext
  	{
  		#region Private Instance Constructors
  
  		/// <summary>
  		/// Private Constructor. 
  		/// </summary>
  		/// <remarks>
  		/// Uses a private access modifier to prevent instantiation of this class.
  		/// </remarks>
  		private ThreadContext()
  		{
  		}
  
  		#endregion Private Instance Constructors
  
  		#region Public Static Properties
  
  		/// <summary>
  		/// The thread properties map
  		/// </summary>
  		/// <value>
  		/// The thread properties map
  		/// </value>
  		public static ThreadContextProperties Properties
  		{
  			get { return s_properties; }
  		}
  
  		/// <summary>
  		/// The thread stacks
  		/// </summary>
  		/// <value>
  		/// stack map
  		/// </value>
  		public static ThreadContextStacks Stacks
  		{
  			get { return s_stacks; }
  		}
  
  		/// <summary>
  		/// The thread lists
  		/// </summary>
  		/// <value>
  		/// list map
  		/// </value>
  		public static ThreadContextLists Lists
  		{
  			get { return s_lists; }
  		}
  
  		#endregion Public Static Properties
  
  		#region Private Static Fields
  
  		/// <summary>
  		/// The thread context properties instance
  		/// </summary>
  		private readonly static ThreadContextProperties s_properties = new ThreadContextProperties();
  
  		/// <summary>
  		/// The thread context stacks instance
  		/// </summary>
  		private readonly static ThreadContextStacks s_stacks = new ThreadContextStacks();
  
  		/// <summary>
  		/// The thread context lists instance
  		/// </summary>
  		private readonly static ThreadContextLists s_lists = new ThreadContextLists();
  
  		#endregion Private Static Fields
  	}
  }
  
  
  
  1.9       +3 -3      logging-log4net/src/Appender/EventLogAppender.cs
  
  Index: EventLogAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/EventLogAppender.cs,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- EventLogAppender.cs	24 Jul 2004 14:54:02 -0000	1.8
  +++ EventLogAppender.cs	9 Sep 2004 21:53:13 -0000	1.9
  @@ -44,7 +44,7 @@
   	/// <remarks>
   	/// <para>
   	/// The <c>EventID</c> of the event log entry can be
  -	/// set using the <c>EventLogEventID</c> property (<see cref="LoggingEvent.Properties"/>)
  +	/// set using the <c>EventLogEventID</c> property (<see cref="LoggingEvent.EventProperties"/>)
   	/// on the <see cref="LoggingEvent"/>.
   	/// </para>
   	/// <para>
  @@ -203,7 +203,7 @@
   		/// <para>Writes the event to the system event log using the 
   		/// <see cref="ApplicationName"/>.</para>
   		/// 
  -		/// <para>If the event has an <c>EventID</c> property (see <see cref="LoggingEvent.Properties"/>)
  +		/// <para>If the event has an <c>EventID</c> property (see <see cref="LoggingEvent.EventProperties"/>)
   		/// set then this integer will be used as the event log event id.</para>
   		/// 
   		/// <para>
  @@ -218,7 +218,7 @@
   			int eventID = 0;
   
   			// Look for the EventLogEventID property
  -			object eventIDPropertyObj = loggingEvent.Properties["EventID"];
  +			object eventIDPropertyObj = loggingEvent.LookupProperty("EventID");
   			if (eventIDPropertyObj != null)
   			{
   				if (eventIDPropertyObj is int)
  
  
  
  1.6       +0 -15     logging-log4net/src/Appender/RemotingAppender.cs
  
  Index: RemotingAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/RemotingAppender.cs,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- RemotingAppender.cs	30 May 2004 18:01:18 -0000	1.5
  +++ RemotingAppender.cs	9 Sep 2004 21:53:13 -0000	1.6
  @@ -42,10 +42,6 @@
   	/// object to deliver events to is specified by setting the
   	/// appenders <see cref="RemotingAppender.Sink"/> property.</para>
   	/// <para>
  -	/// This appender sets the <c>log4net:HostName</c> property in the 
  -	/// <see cref="LoggingEvent.Properties"/> collection to the name of 
  -	/// the machine on which the event is logged.</para>
  -	/// <para>
   	/// The RemotingAppender buffers events before sending them. This allows it to 
   	/// make more efficient use of the remoting infrastructure.</para>
   	/// <para>
  @@ -249,17 +245,6 @@
   			try
   			{
   				LoggingEvent[] events = (LoggingEvent[])state;
  -
  -				string hostName = SystemInfo.HostName;
  -
  -				// Set the hostname
  -				foreach(LoggingEvent e in events)
  -				{
  -					if (e.Properties[LoggingEvent.HostNameProperty] == null)
  -					{
  -						e.Properties[LoggingEvent.HostNameProperty] = hostName;
  -					}
  -				}
   
   				// Send the events
   				m_sinkObj.LogEvents(events);
  
  
  
  1.6       +0 -9      logging-log4net/src/Appender/SmtpAppender.cs
  
  Index: SmtpAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/SmtpAppender.cs,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- SmtpAppender.cs	11 Aug 2004 20:04:05 -0000	1.5
  +++ SmtpAppender.cs	9 Sep 2004 21:53:13 -0000	1.6
  @@ -233,17 +233,8 @@
   					writer.Write(t);
   				}
   
  -
  -				string hostName = SystemInfo.HostName;
  -
   				for(int i = 0; i < events.Length; i++) 
   				{
  -					// Set the hostname property
  -					if (events[i].Properties[LoggingEvent.HostNameProperty] == null)
  -					{
  -						events[i].Properties[LoggingEvent.HostNameProperty] = hostName;
  -					}
  -
   					// Render the event and append the text to the buffer
   					RenderLoggingEvent(writer, events[i]);
   				}
  
  
  
  1.3       +1 -14     logging-log4net/src/Appender/SmtpPickupDirAppender.cs
  
  Index: SmtpPickupDirAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/SmtpPickupDirAppender.cs,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- SmtpPickupDirAppender.cs	16 Feb 2004 02:10:51 -0000	1.2
  +++ SmtpPickupDirAppender.cs	9 Sep 2004 21:53:13 -0000	1.3
  @@ -46,11 +46,6 @@
   	/// cyclic buffer. This keeps memory requirements at a reasonable level while 
   	/// still delivering useful application context.
   	/// </para>
  -	/// <para>
  -	/// This appender sets the <c>log4net:HostName</c> property in the 
  -	/// <see cref="LoggingEvent.Properties"/> collection to the name of 
  -	/// the machine on which the event is logged.
  -	/// </para>
   	/// </remarks>
   	/// <author>Niall Daley</author>
   	/// <author>Nicko Cadell</author>
  @@ -125,7 +120,7 @@
   		override protected void SendBuffer(LoggingEvent[] events) 
   		{
   			// Note: this code already owns the monitor for this
  -			// appender. This frees us from needing to synchronize on 'cb'.
  +			// appender. This frees us from needing to synchronize again.
   			try 
   			{	  
   				using(StreamWriter writer = File.CreateText(Path.Combine(m_pickupDir, SystemInfo.NewGuid().ToString("N"))))
  @@ -141,16 +136,8 @@
   						writer.Write(t);
   					}
   
  -					string hostName = SystemInfo.HostName;
  -
   					for(int i = 0; i < events.Length; i++) 
   					{
  -						// Set the hostname property
  -						if (events[i].Properties[LoggingEvent.HostNameProperty] == null)
  -						{
  -							events[i].Properties[LoggingEvent.HostNameProperty] = hostName;
  -						}
  -
   						// Render the event and append the text to the buffer
   						RenderLoggingEvent(writer, events[i]);
   					}
  
  
  
  1.6       +2 -16     logging-log4net/src/Appender/UdpAppender.cs
  
  Index: UdpAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/UdpAppender.cs,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- UdpAppender.cs	7 Jun 2004 01:07:50 -0000	1.5
  +++ UdpAppender.cs	9 Sep 2004 21:53:13 -0000	1.6
  @@ -37,11 +37,6 @@
   	/// UDP guarantees neither that messages arrive, nor that they arrive in the correct order.
   	/// </para>
   	/// <para>
  -	/// This appender sets the <c>log4net:HostName</c> property in the 
  -	/// <see cref="LoggingEvent.Properties"/> collection to the name of 
  -	/// the machine on which the event is logged.
  -	/// </para>
  -	/// <para>
   	/// To view the logging results, a custom application can be developed that listens for logging 
   	/// events.
   	/// </para>
  @@ -306,9 +301,6 @@
   			set { this.m_client = value; }
   		}
   
  -		#endregion Protected Instance Properties
  -
  -		#region Private Instance Properties
   
   		/// <summary>
   		/// Gets or sets the cached remote endpoint to which the logging events should be sent.
  @@ -321,13 +313,13 @@
   		/// with the values of the <see cref="RemoteAddress" /> and <see cref="RemotePort"/>
   		/// properties.
   		/// </remarks>
  -		private IPEndPoint RemoteEndPoint
  +		protected IPEndPoint RemoteEndPoint
   		{
   			get { return this.m_remoteEndPoint; }
   			set { this.m_remoteEndPoint = value; }
   		}
   
  -		#endregion Private Instance Properties
  +		#endregion Protected Instance Properties
   
   		#region Implementation of IOptionHandler
   
  @@ -402,12 +394,6 @@
   		/// </remarks>
   		protected override void Append(LoggingEvent loggingEvent) 
   		{
  -			// Set the hostname property
  -			if (loggingEvent.Properties[LoggingEvent.HostNameProperty] == null)
  -			{
  -				loggingEvent.Properties[LoggingEvent.HostNameProperty] = SystemInfo.HostName;
  -			}
  -
   			try 
   			{
   				Byte [] buffer = m_encoding.GetBytes(RenderLoggingEvent(loggingEvent).ToCharArray());
  
  
  
  1.10      +184 -129  logging-log4net/src/Core/LoggingEvent.cs
  
  Index: LoggingEvent.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Core/LoggingEvent.cs,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- LoggingEvent.cs	22 Aug 2004 19:26:50 -0000	1.9
  +++ LoggingEvent.cs	9 Sep 2004 21:53:13 -0000	1.10
  @@ -55,11 +55,6 @@
   		public string Ndc;
   
   		/// <summary>
  -		/// The local cache of the MDC dictionary
  -		/// </summary>
  -		public IDictionary Mdc;
  -
  -		/// <summary>
   		/// The application supplied message of logging event.
   		/// </summary>
   		public string Message;
  @@ -115,14 +110,6 @@
   		/// </remarks>
   		public PropertiesDictionary Properties;
   
  -		/// <summary>
  -		/// Global properties
  -		/// </summary>
  -		/// <remarks>
  -		/// Global properties are defined on the <see cref="GlobalContext"/>
  -		/// </remarks>
  -		public ReadOnlyPropertiesDictionary GlobalProperties;
  -
   		#endregion Public Instance Fields
   	}
   
  @@ -135,6 +122,7 @@
   		/// <summary>
   		/// Fix the MDC
   		/// </summary>
  +		[Obsolete("Replaced by composite Properties")]
   		Mdc = 0x01,
   
   		/// <summary>
  @@ -187,6 +175,11 @@
   		Exception = 0x100,
   
   		/// <summary>
  +		/// Fix the event properties
  +		/// </summary>
  +		Properties = 0x200,
  +
  +		/// <summary>
   		/// No fields fixed
   		/// </summary>
   		None = 0x0,
  @@ -204,15 +197,15 @@
   		/// This set of partial fields gives good performance. The following fields are fixed:
   		/// </para>
   		/// <list type="bullet">
  -		/// <item><description><see cref="Mdc"/></description></item>
   		/// <item><description><see cref="Ndc"/></description></item>
   		/// <item><description><see cref="Message"/></description></item>
   		/// <item><description><see cref="ThreadName"/></description></item>
   		/// <item><description><see cref="Exception"/></description></item>
   		/// <item><description><see cref="Domain"/></description></item>
  +		/// <item><description><see cref="Properties"/></description></item>
   		/// </list>
   		/// </remarks>
  -		Partial = Mdc | Ndc | Message | ThreadName | Exception | Domain,
  +		Partial = Ndc | Message | ThreadName | Exception | Domain | Properties,
   	}
   
   	/// <summary>
  @@ -281,9 +274,6 @@
   
   			// Store the event creation time
   			m_data.TimeStamp = DateTime.Now;
  -
  -			// Lookup the global properties as soon as possible
  -			m_data.GlobalProperties = log4net.GlobalContext.Properties.GetReadOnlyProperties();
   		}
   
   		/// <summary>
  @@ -354,7 +344,6 @@
   			m_data.Level = (Level)info.GetValue("Level", typeof(Level));
   
   			m_data.Ndc = info.GetString("Ndc");
  -			m_data.Mdc = (IDictionary) info.GetValue("Mdc", typeof(IDictionary));
   			m_data.Message = info.GetString("Message");
   			m_data.ThreadName = info.GetString("ThreadName");
   			m_data.TimeStamp = info.GetDateTime("TimeStamp");
  @@ -362,7 +351,6 @@
   			m_data.UserName = info.GetString("UserName");
   			m_data.ExceptionString = info.GetString("ExceptionString");
   			m_data.Properties = (PropertiesDictionary) info.GetValue("Properties", typeof(PropertiesDictionary));
  -			m_data.GlobalProperties = (ReadOnlyPropertiesDictionary) info.GetValue("GlobalProperties", typeof(ReadOnlyPropertiesDictionary));
   			m_data.Domain = info.GetString("Domain");
   			m_data.Identity = info.GetString("Identity");
   		}
  @@ -465,43 +453,27 @@
   			}
   		}
   
  -		/// <summary>
  -		/// Gets the text of the <see cref="NDC"/>.
  -		/// </summary>
  -		/// <value>
  -		/// The text of the <see cref="NDC"/>.
  -		/// </value>
  -		public string NestedContext
  -		{
  -			get
  -			{
  -				if (m_data.Ndc == null) 
  -				{
  -					m_data.Ndc = NDC.Get();
  -					if (m_data.Ndc == null)
  -					{
  -						m_data.Ndc = "";
  -					}
  -				}
  -				return m_data.Ndc; 
  -			}
  -		}
  -
  -		/// <summary>
  -		/// Get the MDC dictionary.
  -		/// </summary>
  -		public IDictionary MappedContext
  -		{
  -			get
  -			{
  -				if (m_data.Mdc == null)
  -				{
  -					// This creates a live copy of the MDC
  -					m_data.Mdc = MDC.GetMap();
  -				}
  -				return m_data.Mdc;
  -			}
  -		}
  +//		/// <summary>
  +//		/// Gets the text of the <see cref="ThreadContext.Stacks"/>.
  +//		/// </summary>
  +//		/// <value>
  +//		/// The text of the <see cref="ThreadContext.Stacks"/>.
  +//		/// </value>
  +//		public string NestedContext
  +//		{
  +//			get
  +//			{
  +//				if (m_data.Ndc == null) 
  +//				{
  +//					m_data.Ndc = ThreadContext.Stack.GetFullMessage();
  +//					if (m_data.Ndc == null)
  +//					{
  +//						m_data.Ndc = "";
  +//					}
  +//				}
  +//				return m_data.Ndc; 
  +//			}
  +//		}
   
   		/// <summary>
   		/// Gets the message object used to initialize this event.
  @@ -683,7 +655,8 @@
   							// some undefined set of SecurityPermission flags.
   							LogLog.Debug("LoggingEvent: Security exception while trying to get current thread ID. Error Ignored. Empty thread name.");
   
  -							m_data.ThreadName = "";
  +							// As a last resort use the hash code of the Thread object
  +							m_data.ThreadName = System.Threading.Thread.CurrentThread.GetHashCode().ToString();
   						}
   					}
   #endif
  @@ -836,45 +809,27 @@
   		/// Additional event specific properties.
   		/// </value>
   		/// <remarks>
  +		/// <para>
   		/// A logger or an appender may attach additional
   		/// properties to specific events. These properties
   		/// have a string key and an object value.
  +		/// </para>
  +		/// <para>
  +		/// This property is for events that have been added directly to
  +		/// this event. The aggregate properties (which include these
  +		/// event properties) can be retrieved using <see cref="LookupProperty"/>
  +		/// and <see cref="GetProperties"/>.
  +		/// </para>
   		/// </remarks>
  -		public PropertiesDictionary Properties
  -		{
  -			get 
  -			{ 
  -				if (m_data.Properties == null)
  -				{
  -					m_data.Properties = new PropertiesDictionary();
  -				}
  -				return m_data.Properties; 
  -			}
  -		}
  -
  -		/// <summary>
  -		/// Gets the global properties defined when this event was created.
  -		/// </summary>
  -		/// <value>
  -		/// Globally defined properties.
  -		/// </value>
  -		/// <remarks>
  -		/// Global properties are defined by the <see cref="GlobalContext"/>
  -		/// </remarks>
  -		public ReadOnlyPropertiesDictionary GlobalProperties
  +		public PropertiesDictionary EventProperties
   		{
   			get 
   			{ 
  -				// The global properties are captured in the constructor
  -				// because they are global shared state they must be captured as soon as possible
  -
  -				if (m_data.GlobalProperties == null)
  +				if (m_eventProperties == null)
   				{
  -					// Just in case for some reason this is null set it to an empty collection
  -					// callers do not expect this property to return null
  -					m_data.GlobalProperties = new ReadOnlyPropertiesDictionary();
  +					m_eventProperties = new PropertiesDictionary();
   				}
  -				return m_data.GlobalProperties; 
  +				return m_eventProperties; 
   			}
   		}
   
  @@ -926,7 +881,6 @@
   			info.AddValue("LoggerName", m_data.LoggerName);
   			info.AddValue("Level", m_data.Level);
   			info.AddValue("Ndc", m_data.Ndc);
  -			info.AddValue("Mdc", m_data.Mdc);
   			info.AddValue("Message", m_data.Message);
   			info.AddValue("ThreadName", m_data.ThreadName);
   			info.AddValue("TimeStamp", m_data.TimeStamp);
  @@ -934,7 +888,6 @@
   			info.AddValue("UserName", m_data.UserName);
   			info.AddValue("ExceptionString", m_data.ExceptionString);
   			info.AddValue("Properties", m_data.Properties);
  -			info.AddValue("GlobalProperties", m_data.GlobalProperties);
   			info.AddValue("Domain", m_data.Domain);
   			info.AddValue("Identity", m_data.Identity);
   		}
  @@ -978,23 +931,6 @@
   		}
   
   		/// <summary>
  -		/// Looks up the specified key in the <see cref="MDC"/>.
  -		/// </summary>
  -		/// <param name="key">The key to lookup.</param>
  -		/// <returns>
  -		/// The value associated with the key, or <c>null</c> if the key was not found.
  -		/// </returns>
  -		public string LookupMappedContext(string key)
  -		{
  -			if (m_data.Mdc == null)
  -			{
  -				// This creates a live copy of the MDC
  -				m_data.Mdc = MDC.GetMap();
  -			}
  -			return m_data.Mdc[key] as string;
  -		}
  -
  -		/// <summary>
   		/// Returns this event's exception's rendered using the 
   		/// <see cref="ILoggerRepository.RendererMap" />.
   		/// </summary>
  @@ -1131,20 +1067,13 @@
   
   			if (updateFlags > 0)
   			{
  -				if ((updateFlags & FixFlags.Mdc) != 0)
  -				{
  -					// Force the MDC to be cached
  -					CacheMappedContext();
  -
  -					m_fixFlags |= FixFlags.Mdc;
  -				}
  -				if ((updateFlags & FixFlags.Ndc) != 0)
  -				{
  -					// Force the NDC to be cached
  -					string tmp = this.NestedContext;
  -
  -					m_fixFlags |= FixFlags.Ndc;
  -				}
  +//				if ((updateFlags & FixFlags.Ndc) != 0)
  +//				{
  +//					// Force the NDC to be cached
  +//					string tmp = this.NestedContext;
  +//
  +//					m_fixFlags |= FixFlags.Ndc;
  +//				}
   				if ((updateFlags & FixFlags.Message) != 0)
   				{
   					// Force the message to be rendered
  @@ -1203,6 +1132,13 @@
   
   					m_fixFlags |= FixFlags.Exception;
   				}
  +
  +				if ((updateFlags & FixFlags.Properties) != 0)
  +				{
  +					CacheProperties();
  +
  +					m_fixFlags |= FixFlags.Properties;
  +				}
   			}
   		}
   
  @@ -1210,16 +1146,125 @@
   
   		#region Protected Instance Methods
   
  +		private void CreateCompositeProperties()
  +		{
  +			m_compositeProperties = new CompositeProperties();
  +
  +			if (m_eventProperties != null)
  +			{
  +				m_compositeProperties.Add(m_eventProperties);
  +			}
  +			m_compositeProperties.Add(ThreadContext.Properties.GetProperties());
  +
  +			// TODO: Add Repository Properties
  +
  +			m_compositeProperties.Add(GlobalContext.Properties.GetReadOnlyProperties());
  +		}
  +
  +		private void CacheProperties()
  +		{
  +			if (m_data.Properties == null)
  +			{
  +				if (m_compositeProperties == null)
  +				{
  +					CreateCompositeProperties();
  +				}
  +
  +				PropertiesDictionary flattenedProperties = m_compositeProperties.Flatten();
  +
  +				PropertiesDictionary fixedProperties = new PropertiesDictionary();
  +
  +				// Fix any IFixingRequired objects
  +				foreach(DictionaryEntry entry in flattenedProperties)
  +				{
  +					string key = (string)entry.Key;
  +					object val = entry.Value;
  +
  +					IFixingRequired fixingRequired = val as IFixingRequired;
  +					if (fixingRequired != null)
  +					{
  +						val = fixingRequired.GetFixedObject();
  +					}
  +
  +					fixedProperties[key] = val;
  +				}
  +
  +				m_data.Properties = fixedProperties;
  +			}
  +		}
  +
  +		/// <summary>
  +		/// Lookup a composite property in this event
  +		/// </summary>
  +		/// <param name="key">the key for the property to lookup</param>
  +		/// <returns>the value for the property</returns>
  +		/// <remarks>
  +		/// <para>
  +		/// This event has composite properties that combine together properties from
  +		/// several different contexts in the following order:
  +		/// <list type="definition">
  +		///		<item>
  +		/// 		<term>this events properties</term>
  +		/// 		<description>
  +		/// 		This event has <see cref="EventProperties"/> that can be set. These 
  +		/// 		properties are specific to this event only.
  +		/// 		</description>
  +		/// 	</item>
  +		/// 	<item>
  +		/// 		<term>the thread properties</term>
  +		/// 		<description>
  +		/// 		The <see cref="ThreadContext.Properties"/> that are set on the current
  +		/// 		thread. These properties are shared by all events logged on this thread.
  +		/// 		</description>
  +		/// 	</item>
  +		/// 	<item>
  +		/// 		<term>the global properties</term>
  +		/// 		<description>
  +		/// 		The <see cref="GlobalContext.Properties"/> that are set globally. These 
  +		/// 		properties are shared by all the threads in the AppDomain.
  +		/// 		</description>
  +		/// 	</item>
  +		/// </list>
  +		/// </para>
  +		/// </remarks>
  +		public object LookupProperty(string key)
  +		{
  +			if (m_data.Properties != null)
  +			{
  +				return m_data.Properties[key];
  +			}
  +			if (m_compositeProperties == null)
  +			{
  +				CreateCompositeProperties();
  +			}
  +			return m_compositeProperties[key];
  +		}
  +
   		/// <summary>
  -		/// Creates a cached copy of the <see cref="MDC" />.
  +		/// Get all the composite properties in this event
   		/// </summary>
  -		protected void CacheMappedContext()
  +		/// <returns>the <see cref="PropertiesDictionary"/> containing all the properties</returns>
  +		/// <remarks>
  +		/// <para>
  +		/// See <see cref="LookupProperty"/> for details of the composite properties 
  +		/// stored by the event.
  +		/// </para>
  +		/// <para>
  +		/// This method returns a single <see cref="PropertiesDictionary"/> containing all the
  +		/// properties defined for this event.
  +		/// </para>
  +		/// </remarks>
  +		public PropertiesDictionary GetProperties()
   		{
  -			// Copy the MDC dictionary
  -			if (m_data.Mdc == null || m_data.Mdc.IsReadOnly == false)
  +			if (m_data.Properties != null)
  +			{
  +				return m_data.Properties;
  +			}
  +			if (m_compositeProperties == null)
   			{
  -				m_data.Mdc = MDC.CopyMap();
  +				CreateCompositeProperties();
   			}
  +			return m_compositeProperties.Flatten();
   		}
   
   		#endregion Public Instance Methods
  @@ -1232,6 +1277,16 @@
   		private LoggingEventData m_data;
   
   		/// <summary>
  +		/// The internal logging event data.
  +		/// </summary>
  +		private CompositeProperties m_compositeProperties;
  +
  +		/// <summary>
  +		/// The internal logging event data.
  +		/// </summary>
  +		private PropertiesDictionary m_eventProperties;
  +
  +		/// <summary>
   		/// The fully qualified classname of the calling 
   		/// logger class.
   		/// </summary>
  @@ -1273,17 +1328,17 @@
   		#region Constants
   
   		/// <summary>
  -		/// The key into the <see cref="Properties"/> map for the host name value.
  +		/// The key into the Properties map for the host name value.
   		/// </summary>
   		public const string HostNameProperty = "log4net:HostName";
   
   		/// <summary>
  -		/// The key into the <see cref="Properties"/> map for the thread identity value.
  +		/// The key into the Properties map for the thread identity value.
   		/// </summary>
   		public const string IdentityProperty = "log4net:Identity";
   
   		/// <summary>
  -		/// The key into the <see cref="Properties"/> map for the user name value.
  +		/// The key into the Properties map for the user name value.
   		/// </summary>
   		public const string UserNameProperty = "log4net:UserName";
   
  
  
  
  1.1                  logging-log4net/src/Core/IFixingRequired.cs
  
  Index: IFixingRequired.cs
  ===================================================================
  #region Copyright & License
  //
  // Copyright 2001-2004 The Apache Software Foundation
  //
  // Licensed 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;
  
  namespace log4net.Core
  {
  	/// <summary>
  	/// Interface that indicates that the object requires fixing before it
  	/// can be taken outside the context of the appender's DoAppend method.
  	/// </summary>
  	/// <author>Nicko Cadell</author>
  	public interface IFixingRequired
  	{
  		/// <summary>
  		/// Get a portable version of this object
  		/// </summary>
  		/// <returns>the portable instance of this object</returns>
  		object GetFixedObject();
  	}
  }
  
  
  
  1.5       +5 -2      logging-log4net/src/Filter/MdcFilter.cs
  
  Index: MdcFilter.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Filter/MdcFilter.cs,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- MdcFilter.cs	30 May 2004 11:04:08 -0000	1.4
  +++ MdcFilter.cs	9 Sep 2004 21:53:13 -0000	1.5
  @@ -180,9 +180,12 @@
   				return FilterDecision.Neutral;
   			}
   
  -			// Lookup the string to match in from the MDC using 
  +			// Lookup the string to match in from the properties using 
   			// the key specified.
  -			string msg = loggingEvent.LookupMappedContext(m_key);
  +			object msgObj = loggingEvent.LookupProperty(m_key);
  +
  +			// Use an ObjectRenderer to convert the property value to a string
  +			string msg = loggingEvent.Repository.RendererMap.FindAndRender(msgObj);
   
   			// Check if we have been setup to filter
   			if (msg == null || (m_stringToMatch == null && m_regexToMatch == null))
  
  
  
  1.5       +11 -1     logging-log4net/src/Filter/NdcFilter.cs
  
  Index: NdcFilter.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Filter/NdcFilter.cs,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- NdcFilter.cs	30 May 2004 11:04:08 -0000	1.4
  +++ NdcFilter.cs	9 Sep 2004 21:53:13 -0000	1.5
  @@ -156,7 +156,17 @@
   				throw new ArgumentNullException("loggingEvent");
   			}
   
  -			string msg = loggingEvent.NestedContext;
  +			string msg = null;
  +
  +			object msgObj = loggingEvent.LookupProperty("NDC");
  +			if (msgObj is string)
  +			{
  +				msg = (string)msgObj;
  +			}
  +			else if (msgObj is ThreadContextStack)
  +			{
  +				msg = ((ThreadContextStack)msgObj).GetFullMessage();
  +			}
   
   			// Check if we have been setup to filter
   			if (msg == null || (m_stringToMatch == null && m_regexToMatch == null))
  
  
  
  1.1                  logging-log4net/src/Filter/PropertyFilter.cs
  
  Index: PropertyFilter.cs
  ===================================================================
  #region Copyright & License
  //
  // Copyright 2001-2004 The Apache Software Foundation
  //
  // Licensed 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.Text.RegularExpressions;
  
  using log4net;
  using log4net.Core;
  using log4net.Util;
  
  namespace log4net.Filter
  {
  	/// <summary>
  	/// Simple filter to match a string in the event properties
  	/// </summary>
  	/// <remarks>
  	/// Simple filter to match a string in the event properties
  	/// </remarks>
  	/// <author>Nicko Cadell</author>
  	public class PropertyFilter : FilterSkeleton
  	{
  		#region Member Variables
  
  		/// <summary>
  		/// Flag to indicate the behavior when we have a match
  		/// </summary>
  		private bool m_acceptOnMatch = true;
  
  		/// <summary>
  		/// The string to substring match against the message
  		/// </summary>
  		private string m_stringToMatch;
  
  		/// <summary>
  		/// A string regex to match
  		/// </summary>
  		private string m_stringRegexToMatch;
  
  		/// <summary>
  		/// A regex object to match (generated from m_stringRegexToMatch)
  		/// </summary>
  		private Regex m_regexToMatch;
  
  		/// <summary>
  		/// The key to use to lookup the string from the event properties
  		/// </summary>
  		private string m_key;
  
  		#endregion
  
  		#region Constructors
  
  		/// <summary>
  		/// Default constructor
  		/// </summary>
  		public PropertyFilter()
  		{
  		}
  
  		#endregion
  
  		#region Implementation of IOptionHandler
  
  		/// <summary>
  		/// Initialize and precompile the Regex if required
  		/// </summary>
  		/// <remarks>
  		/// <para>
  		/// This is part of the <see cref="IOptionHandler"/> delayed object
  		/// activation scheme. The <see cref="ActivateOptions"/> method must 
  		/// be called on this object after the configuration properties have
  		/// been set. Until <see cref="ActivateOptions"/> is called this
  		/// object is in an undefined state and must not be used. 
  		/// </para>
  		/// <para>
  		/// If any of the configuration properties are modified then 
  		/// <see cref="ActivateOptions"/> must be called again.
  		/// </para>
  		/// </remarks>
  		override public void ActivateOptions() 
  		{
  			if (m_stringRegexToMatch != null)
  			{
  				m_regexToMatch = new Regex(m_stringRegexToMatch, RegexOptions.Compiled);
  			}
  		}
  
  		#endregion
  
  		/// <summary>
  		/// The <see cref="AcceptOnMatch"/> property is a flag that determines
  		/// the behavior when a matching <see cref="Level"/> is found. If the
  		/// flag is set to true then the filter will <see cref="FilterDecision.Accept"/> the 
  		/// logging event, otherwise it will <see cref="FilterDecision.Deny"/> the event.
  		/// </summary>
  		public bool AcceptOnMatch
  		{
  			get { return m_acceptOnMatch; }
  			set { m_acceptOnMatch = value; }
  		}
  
  		/// <summary>
  		/// The string that will be substring matched against
  		/// the rendered message. If the message contains this
  		/// string then the filter will match.
  		/// </summary>
  		public string StringToMatch
  		{
  			get { return m_stringToMatch; }
  			set { m_stringToMatch = value; }
  		}
  
  		/// <summary>
  		/// The regular expression pattern that will be matched against
  		/// the rendered message. If the message matches this
  		/// pattern then the filter will match.
  		/// </summary>
  		public string RegexToMatch
  		{
  			get { return m_stringRegexToMatch; }
  			set { m_stringRegexToMatch = value; }
  		}
  
  		/// <summary>
  		/// The key to lookup in the event properties and then match against.
  		/// </summary>
  		public string Key
  		{
  			get { return m_key; }
  			set { m_key = value; }
  		}
  
  		#region Override implementation of FilterSkeleton
  
  		/// <summary>
  		/// Check if this filter should allow the event to be logged
  		/// </summary>
  		/// <remarks>
  		/// The event property for the <see cref="Key"/> is matched against 
  		/// the <see cref="StringToMatch"/>.
  		/// If the <see cref="StringToMatch"/> occurs as a substring within
  		/// the property value then a match will have occurred. If no match occurs
  		/// this function will return <see cref="FilterDecision.Neutral"/>
  		/// allowing other filters to check the event. If a match occurs then
  		/// the value of <see cref="AcceptOnMatch"/> is checked. If it is
  		/// true then <see cref="FilterDecision.Accept"/> is returned otherwise
  		/// <see cref="FilterDecision.Deny"/> is returned.
  		/// </remarks>
  		/// <param name="loggingEvent">the event being logged</param>
  		/// <returns>see remarks</returns>
  		override public FilterDecision Decide(LoggingEvent loggingEvent) 
  		{
  			if (loggingEvent == null)
  			{
  				throw new ArgumentNullException("loggingEvent");
  			}
  
  			// Check if we have a key to lookup the event property value with
  			if (m_key == null)
  			{
  				// We cannot filter so allow the filter chain
  				// to continue processing
  				return FilterDecision.Neutral;
  			}
  
  			// Lookup the string to match in from the properties using 
  			// the key specified.
  			object msgObj = loggingEvent.LookupProperty(m_key);
  
  			// Use an ObjectRenderer to convert the property value to a string
  			string msg = loggingEvent.Repository.RendererMap.FindAndRender(msgObj);
  
  			// Check if we have been setup to filter
  			if (msg == null || (m_stringToMatch == null && m_regexToMatch == null))
  			{
  				// We cannot filter so allow the filter chain
  				// to continue processing
  				return FilterDecision.Neutral;
  			}
      
  			// Firstly check if we are matching using a regex
  			if (m_regexToMatch != null)
  			{
  				// Check the regex
  				if (m_regexToMatch.Match(msg).Success == false)
  				{
  					// No match, continue processing
  					return FilterDecision.Neutral;
  				} 
  
  				// we've got a match
  				if (m_acceptOnMatch) 
  				{
  					return FilterDecision.Accept;
  				} 
  				return FilterDecision.Deny;
  			}
  			else if (m_stringToMatch != null)
  			{
  				// Check substring match
  				if (msg.IndexOf(m_stringToMatch) == -1) 
  				{
  					// No match, continue processing
  					return FilterDecision.Neutral;
  				} 
  
  				// we've got a match
  				if (m_acceptOnMatch) 
  				{
  					return FilterDecision.Accept;
  				} 
  				return FilterDecision.Deny;
  			}
  			return FilterDecision.Neutral;
  		}
  
  		#endregion
  	}
  }
  
  
  
  1.10      +43 -34    logging-log4net/src/Layout/PatternLayout.cs
  
  Index: PatternLayout.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Layout/PatternLayout.cs,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- PatternLayout.cs	19 Aug 2004 21:18:47 -0000	1.9
  +++ PatternLayout.cs	9 Sep 2004 21:53:13 -0000	1.10
  @@ -164,24 +164,6 @@
   	///			</description>
   	///		</item>
   	///		<item>
  -	///			<term>global</term>
  -	///			<description>
  -	/// 			<para>
  -	/// 			Used to output the a global property. The key to 
  -	/// 			lookup must be specified within braces and directly following the
  -	/// 			pattern specifier, e.g. <b>%global{user}</b> would include the value
  -	/// 			from the property that is keyed by the string 'user'. Each property value
  -	/// 			that is to be included in the log must be specified separately.
  -	/// 			Properties are added to events by loggers or appenders. By default
  -	/// 			no properties are defined.
  -	/// 			</para>
  -	/// 			<para>
  -	/// 			If no key is specified, e.g. <b>%global</b> then all the keys and their
  -	/// 			values are printed in a comma separated list.
  -	/// 			</para>
  -	///			</description>
  -	///		</item>
  -	///		<item>
   	///			<term>identity</term>
   	///			<description>
   	///				<para>
  @@ -292,16 +274,9 @@
   	///			<term>mdc</term>
   	///			<description>
   	/// 			<para>
  -	/// 			Used to output the MDC (mapped diagnostic context) associated
  -	/// 			with the thread that generated the logging event. The key to lookup
  -	/// 			must be specified within braces and directly following the
  -	/// 			pattern specifier, e.g. <c>%mdc{user}</c> would include the value
  -	/// 			from the MDC that is keyed by the string 'user'. Each MDC value
  -	/// 			that is to be included in the log must be specified separately.
  -	/// 			</para>
  -	/// 			<para>
  -	/// 			If no key is specified, e.g. <b>%mdc</b> then all the keys and their
  -	/// 			values are printed in a comma separated list.
  +	/// 			The MDC (old name for the ThreadContext.Properties) is now part of the
  +	/// 			combined event properties. This pattern is supported for compatibility
  +	/// 			but is equivalent to <b>property</b>.
   	/// 			</para>
   	///			</description>
   	///		</item>
  @@ -357,6 +332,10 @@
   	///         <term>P</term>
   	///         <description>Equivalent to <b>property</b></description>
   	///     </item>
  +	///     <item>
  +	///         <term>properties</term>
  +	///         <description>Equivalent to <b>property</b></description>
  +	///     </item>
   	///		<item>
   	///			<term>property</term>
   	///			<description>
  @@ -366,13 +345,42 @@
   	/// 			pattern specifier, e.g. <b>%property{user}</b> would include the value
   	/// 			from the property that is keyed by the string 'user'. Each property value
   	/// 			that is to be included in the log must be specified separately.
  -	/// 			Properties are added to events by loggers or appenders. By default
  -	/// 			no properties are defined.
  +	/// 			Properties are added to events by loggers or appenders. By default 
  +	/// 			the <c>log4net:HostName</c> property is set to the name of machine on 
  +	/// 			which the event was originally logged.
   	/// 			</para>
   	/// 			<para>
   	/// 			If no key is specified, e.g. <b>%property</b> then all the keys and their
   	/// 			values are printed in a comma separated list.
   	/// 			</para>
  +	/// 			<para>
  +	/// 			The properties of an event are combined from a number of different
  +	/// 			contexts. These are listed below in the order in which they are searched.
  +	/// 			</para>
  +	/// 			<list type="definition">
  +	/// 				<item>
  +	/// 					<term>the event properties</term>
  +	/// 					<description>
  +	/// 					The event has <see cref="LoggingEvent.EventProperties"/> that can be set. These 
  +	/// 					properties are specific to this event only.
  +	/// 					</description>
  +	/// 				</item>
  +	/// 				<item>
  +	/// 					<term>the thread properties</term>
  +	/// 					<description>
  +	/// 					The <see cref="ThreadContext.Properties"/> that are set on the current
  +	/// 					thread. These properties are shared by all events logged on this thread.
  +	/// 					</description>
  +	/// 				</item>
  +	/// 				<item>
  +	/// 					<term>the global properties</term>
  +	/// 					<description>
  +	/// 					The <see cref="GlobalContext.Properties"/> that are set globally. These 
  +	/// 					properties are shared by all the threads in the AppDomain.
  +	/// 					</description>
  +	/// 				</item>
  +	/// 			</list> 			
  +	/// 			
   	///			</description>
   	///		</item>
   	///     <item>
  @@ -702,8 +710,6 @@
   			s_globalRulesRegistry.Add("F", typeof(FileLocationPatternConverter));
   			s_globalRulesRegistry.Add("file", typeof(FileLocationPatternConverter));
   
  -			s_globalRulesRegistry.Add("global", typeof(GlobalPropertyPatternConverter));
  -
   			s_globalRulesRegistry.Add("l", typeof(FullLocationPatternConverter));
   			s_globalRulesRegistry.Add("location", typeof(FullLocationPatternConverter));
   
  @@ -721,6 +727,7 @@
   
   			s_globalRulesRegistry.Add("P", typeof(PropertyPatternConverter));
   			s_globalRulesRegistry.Add("property", typeof(PropertyPatternConverter));
  +			s_globalRulesRegistry.Add("properties", typeof(PropertyPatternConverter));
   
   			s_globalRulesRegistry.Add("r", typeof(RelativeTimePatternConverter));
   			s_globalRulesRegistry.Add("timestamp", typeof(RelativeTimePatternConverter));
  @@ -728,11 +735,13 @@
   			s_globalRulesRegistry.Add("t", typeof(ThreadPatternConverter));
   			s_globalRulesRegistry.Add("thread", typeof(ThreadPatternConverter));
   
  +			// For backwards compatibility the NDC patters
   			s_globalRulesRegistry.Add("x", typeof(NdcPatternConverter));
   			s_globalRulesRegistry.Add("ndc", typeof(NdcPatternConverter));
   
  -			s_globalRulesRegistry.Add("X", typeof(MdcPatternConverter));
  -			s_globalRulesRegistry.Add("mdc", typeof(MdcPatternConverter));
  +			// For backwards compatibility the MDC patters just do a property lookup
  +			s_globalRulesRegistry.Add("X", typeof(PropertyPatternConverter));
  +			s_globalRulesRegistry.Add("mdc", typeof(PropertyPatternConverter));
   
   			s_globalRulesRegistry.Add("a", typeof(AppDomainPatternConverter));
   			s_globalRulesRegistry.Add("appdomain", typeof(AppDomainPatternConverter));
  
  
  
  1.3       +2 -2      logging-log4net/src/Layout/RawPropertyLayout.cs
  
  Index: RawPropertyLayout.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Layout/RawPropertyLayout.cs,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- RawPropertyLayout.cs	16 Feb 2004 02:10:53 -0000	1.2
  +++ RawPropertyLayout.cs	9 Sep 2004 21:53:13 -0000	1.3
  @@ -49,7 +49,7 @@
   		private string m_key;
   
   		/// <summary>
  -		/// The name of the value to lookup in the <see cref="LoggingEvent.Properties"/> collection.
  +		/// The name of the value to lookup in the LoggingEvent Properties collection.
   		/// </summary>
   		public string Key
   		{
  @@ -69,7 +69,7 @@
   		/// </remarks>
   		public virtual object Format(LoggingEvent loggingEvent)
   		{
  -			return loggingEvent.Properties[m_key];
  +			return loggingEvent.LookupProperty(m_key);
   		}
   
   		#endregion
  
  
  
  1.7       +9 -66     logging-log4net/src/Layout/XMLLayout.cs
  
  Index: XMLLayout.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Layout/XMLLayout.cs,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- XMLLayout.cs	2 Aug 2004 09:44:11 -0000	1.6
  +++ XMLLayout.cs	9 Sep 2004 21:53:13 -0000	1.7
  @@ -153,8 +153,6 @@
   			{
   				m_elmEvent = m_prefix + ":" + ELM_EVENT;
   				m_elmMessage = m_prefix + ":" + ELM_MESSAGE;
  -				m_elmNdc = m_prefix + ":" + ELM_NDC;
  -				m_elmMdc = m_prefix + ":" + ELM_MDC;
   				m_elmProperties = m_prefix + ":" + ELM_PROPERTIES;
   				m_elmGlobalProperties = m_prefix + ":" + ELM_GLOBAL_PROPERTIES;
   				m_elmData = m_prefix + ":" + ELM_DATA;
  @@ -198,75 +196,24 @@
   			Transform.WriteEscapedXmlString(writer, loggingEvent.RenderedMessage);
   			writer.WriteEndElement();
   
  -			if (loggingEvent.NestedContext != null && loggingEvent.NestedContext.Length > 0)
  -			{
  -				// Append the NDC text
  -				writer.WriteStartElement(m_elmNdc);
  -				Transform.WriteEscapedXmlString(writer, loggingEvent.NestedContext);
  -				writer.WriteEndElement();
  -			}
  +			PropertiesDictionary properties = loggingEvent.GetProperties();
   
  -			if (loggingEvent.MappedContext != null && loggingEvent.MappedContext.Count > 0)
  +			// Append the properties text
  +			if (properties.Count > 0)
   			{
  -				// Append the MDC text
  -				writer.WriteStartElement(m_elmMdc);
  -				foreach(System.Collections.DictionaryEntry entry in loggingEvent.MappedContext)
  +				writer.WriteStartElement(m_elmGlobalProperties);
  +				foreach(System.Collections.DictionaryEntry entry in properties)
   				{
   					writer.WriteStartElement(m_elmData);
   					writer.WriteAttributeString(ATTR_NAME, (string)entry.Key);
   
  -					// TODO Should use an ObjectRenderer to convert to a string
  -					writer.WriteAttributeString(ATTR_VALUE, entry.Value.ToString());
  -					writer.WriteEndElement();
  -				}
  -				writer.WriteEndElement();
  -			}
  -
  -			if (loggingEvent.Properties != null)
  -			{
  -				// Append the properties text
  -				string[] propKeys = loggingEvent.Properties.GetKeys();
  -				if (propKeys.Length > 0)
  -				{
  -					writer.WriteStartElement(m_elmProperties);
  -					foreach(string key in propKeys)
  -					{
  -						writer.WriteStartElement(m_elmData);
  -						writer.WriteAttributeString(ATTR_NAME, key);
  -
  -						// TODO Should use an ObjectRenderer to convert to a string
  -						writer.WriteAttributeString(ATTR_VALUE, loggingEvent.Properties[key].ToString());
  -						writer.WriteEndElement();
  -					}
  -					writer.WriteEndElement();
  -				}
  -			}
  +					// Use an ObjectRenderer to convert the object to a string
  +					string valueStr = loggingEvent.Repository.RendererMap.FindAndRender(entry.Value);
  +					writer.WriteAttributeString(ATTR_VALUE, valueStr);
   
  -			if (loggingEvent.GlobalProperties != null)
  -			{
  -				// Append the properties text
  -				string[] propKeys = loggingEvent.Properties.GetKeys();
  -				if (loggingEvent.GlobalProperties.Count > 0)
  -				{
  -					writer.WriteStartElement(m_elmGlobalProperties);
  -					foreach(System.Collections.DictionaryEntry entry in loggingEvent.GlobalProperties)
  -					{
  -						writer.WriteStartElement(m_elmData);
  -						writer.WriteAttributeString(ATTR_NAME, (string)entry.Key);
  -
  -						if (entry.Value == null)
  -						{
  -							writer.WriteAttributeString(ATTR_VALUE, "null");
  -						}
  -						else
  -						{
  -							// TODO Should use an ObjectRenderer to convert to a string
  -							writer.WriteAttributeString(ATTR_VALUE, entry.Value.ToString());
  -						}
  -						writer.WriteEndElement();
  -					}
   					writer.WriteEndElement();
   				}
  +				writer.WriteEndElement();
   			}
   
   			string exceptionStr = loggingEvent.GetExceptionString();
  @@ -304,8 +251,6 @@
   
   		private string m_elmEvent = ELM_EVENT;
   		private string m_elmMessage = ELM_MESSAGE;
  -		private string m_elmNdc = ELM_NDC;
  -		private string m_elmMdc = ELM_MDC;
   		private string m_elmData = ELM_DATA;
   		private string m_elmProperties = ELM_PROPERTIES;
   		private string m_elmGlobalProperties = ELM_GLOBAL_PROPERTIES;
  @@ -320,8 +265,6 @@
   
   		private const string ELM_EVENT = "event";
   		private const string ELM_MESSAGE = "message";
  -		private const string ELM_NDC = "ndc";
  -		private const string ELM_MDC = "mdc";
   		private const string ELM_PROPERTIES = "properties";
   		private const string ELM_GLOBAL_PROPERTIES = "global-properties";
   		private const string ELM_DATA = "data";
  
  
  
  1.7       +31 -49    logging-log4net/src/Layout/XmlLayoutSchemaLog4j.cs
  
  Index: XmlLayoutSchemaLog4j.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Layout/XmlLayoutSchemaLog4j.cs,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- XmlLayoutSchemaLog4j.cs	19 Aug 2004 22:30:57 -0000	1.6
  +++ XmlLayoutSchemaLog4j.cs	9 Sep 2004 21:53:13 -0000	1.7
  @@ -123,6 +123,8 @@
   
   		*/
   
  +		/* Since log4j 1.3 the log4j:MDC has been combined into the log4j:properties element */
  +
   		/// <summary>
   		/// Actually do the writing of the xml
   		/// </summary>
  @@ -132,44 +134,35 @@
   		{
   			// Translate logging events for log4j
   
  -			// Copy global properties
  -			if (loggingEvent.GlobalProperties != null)
  -			{
  -				foreach(System.Collections.DictionaryEntry entry in loggingEvent.GlobalProperties)
  -				{
  -					loggingEvent.Properties[(string)entry.Key] = entry.Value;
  -				}
  -			}
  -
   			// Translate hostname property
  -			if (loggingEvent.Properties[LoggingEvent.HostNameProperty] != null && 
  -				loggingEvent.Properties["log4jmachinename"] == null)
  +			if (loggingEvent.LookupProperty(LoggingEvent.HostNameProperty) != null && 
  +				loggingEvent.LookupProperty("log4jmachinename") == null)
   			{
  -				loggingEvent.Properties["log4jmachinename"] = loggingEvent.Properties[LoggingEvent.HostNameProperty];
  +				loggingEvent.GetProperties()["log4jmachinename"] = loggingEvent.LookupProperty(LoggingEvent.HostNameProperty);
   			}
   
   			// translate appdomain name
  -			if (loggingEvent.Properties["log4japp"] == null && 
  +			if (loggingEvent.LookupProperty("log4japp") == null && 
   				loggingEvent.Domain != null && 
   				loggingEvent.Domain.Length > 0)
   			{
  -				loggingEvent.Properties["log4japp"] = loggingEvent.Domain;
  +				loggingEvent.GetProperties()["log4japp"] = loggingEvent.Domain;
   			}
   
   			// translate identity name
   			if (loggingEvent.Identity != null && 
   				loggingEvent.Identity.Length > 0 && 
  -				loggingEvent.Properties[LoggingEvent.IdentityProperty] == null)
  +				loggingEvent.LookupProperty(LoggingEvent.IdentityProperty) == null)
   			{
  -				loggingEvent.Properties[LoggingEvent.IdentityProperty] = loggingEvent.Identity;
  +				loggingEvent.GetProperties()[LoggingEvent.IdentityProperty] = loggingEvent.Identity;
   			}
   
   			// translate user name
   			if (loggingEvent.UserName != null && 
   				loggingEvent.UserName.Length > 0 && 
  -				loggingEvent.Properties[LoggingEvent.UserNameProperty] == null)
  +				loggingEvent.LookupProperty(LoggingEvent.UserNameProperty) == null)
   			{
  -				loggingEvent.Properties[LoggingEvent.UserNameProperty] = loggingEvent.UserName;
  +				loggingEvent.GetProperties()[LoggingEvent.UserNameProperty] = loggingEvent.UserName;
   			}
   
   			// Write the start element
  @@ -192,48 +185,37 @@
   			Transform.WriteEscapedXmlString(writer, loggingEvent.RenderedMessage);
   			writer.WriteEndElement();
   
  -			if (loggingEvent.NestedContext != null && loggingEvent.NestedContext.Length > 0)
  +			object ndcObj = loggingEvent.LookupProperty("NDC");
  +			if (ndcObj != null)
   			{
  -				// Append the NDC text
  -				writer.WriteStartElement("log4j:NDC");
  -				Transform.WriteEscapedXmlString(writer, loggingEvent.NestedContext);
  -				writer.WriteEndElement();
  -			}
  +				string valueStr = loggingEvent.Repository.RendererMap.FindAndRender(ndcObj);
   
  -			if (loggingEvent.MappedContext != null && loggingEvent.MappedContext.Count > 0)
  -			{
  -				// Append the MDC text
  -				writer.WriteStartElement("log4j:MDC");
  -				foreach(System.Collections.DictionaryEntry entry in loggingEvent.MappedContext)
  +				if (valueStr != null && valueStr.Length > 0)
   				{
  -					writer.WriteStartElement("log4j:data");
  -					writer.WriteAttributeString("name", (string)entry.Key);
  -
  -					// TODO: Use an ObjectRenderer to convert the object to a string
  -					writer.WriteAttributeString("value", entry.Value.ToString());
  +					// Append the NDC text
  +					writer.WriteStartElement("log4j:NDC");
  +					Transform.WriteEscapedXmlString(writer, valueStr);
   					writer.WriteEndElement();
   				}
  -				writer.WriteEndElement();
   			}
   
  -			if (loggingEvent.Properties != null)
  +			// Append the properties text
  +			PropertiesDictionary properties = loggingEvent.GetProperties();
  +			if (properties.Count > 0)
   			{
  -				// Append the properties text
  -				string[] propKeys = loggingEvent.Properties.GetKeys();
  -				if (propKeys.Length > 0)
  +				writer.WriteStartElement("log4j:properties");
  +				foreach(System.Collections.DictionaryEntry entry in properties)
   				{
  -					writer.WriteStartElement("log4j:properties");
  -					foreach(string key in propKeys)
  -					{
  -						writer.WriteStartElement("log4j:data");
  -						writer.WriteAttributeString("name", key);
  -
  -						// TODO: Use an ObjectRenderer to convert the object to a string
  -						writer.WriteAttributeString("value", loggingEvent.Properties[key].ToString());
  -						writer.WriteEndElement();
  -					}
  +					writer.WriteStartElement("log4j:data");
  +					writer.WriteAttributeString("name", (string)entry.Key);
  +
  +					// Use an ObjectRenderer to convert the object to a string
  +					string valueStr = loggingEvent.Repository.RendererMap.FindAndRender(entry.Value);
  +					writer.WriteAttributeString("value", valueStr);
  +
   					writer.WriteEndElement();
   				}
  +				writer.WriteEndElement();
   			}
   
   			string exceptionStr = loggingEvent.GetExceptionString();
  
  
  
  1.3       +2 -1      logging-log4net/src/Layout/Pattern/NdcPatternConverter.cs
  
  Index: NdcPatternConverter.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Layout/Pattern/NdcPatternConverter.cs,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- NdcPatternConverter.cs	16 Feb 2004 02:10:53 -0000	1.2
  +++ NdcPatternConverter.cs	9 Sep 2004 21:53:14 -0000	1.3
  @@ -38,7 +38,8 @@
   		/// <returns>the relevant location information</returns>
   		override protected void Convert(TextWriter writer, LoggingEvent loggingEvent)
   		{
  -			writer.Write( loggingEvent.NestedContext );
  +			// Write the value for the specified key
  +			WriteObject(writer, loggingEvent.Repository, loggingEvent.LookupProperty("NDC"));
   		}
   	}
   }
  
  
  
  1.3       +2 -2      logging-log4net/src/Layout/Pattern/PropertyPatternConverter.cs
  
  Index: PropertyPatternConverter.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Layout/Pattern/PropertyPatternConverter.cs,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- PropertyPatternConverter.cs	16 Feb 2004 02:10:53 -0000	1.2
  +++ PropertyPatternConverter.cs	9 Sep 2004 21:53:14 -0000	1.3
  @@ -43,12 +43,12 @@
   			if (Option != null)
   			{
   				// Write the value for the specified key
  -				WriteObject(writer, loggingEvent.Repository, loggingEvent.Properties[Option]);
  +				WriteObject(writer, loggingEvent.Repository, loggingEvent.LookupProperty(Option));
   			}
   			else
   			{
   				// Write all the key value pairs
  -				WriteDictionary(writer, loggingEvent.Repository, loggingEvent.Properties);
  +				WriteDictionary(writer, loggingEvent.Repository, loggingEvent.GetProperties());
   			}
   		}
   	}
  
  
  
  1.7       +1 -0      logging-log4net/src/Util/PatternString.cs
  
  Index: PatternString.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Util/PatternString.cs,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- PatternString.cs	19 Aug 2004 21:21:32 -0000	1.6
  +++ PatternString.cs	9 Sep 2004 21:53:14 -0000	1.7
  @@ -85,6 +85,7 @@
   			s_globalRulesRegistry.Add("processid", typeof(ProcessIdPatternConverter));
   			s_globalRulesRegistry.Add("random", typeof(RandomStringPatternConverter));
   			s_globalRulesRegistry.Add("username", typeof(UserNamePatternConverter));
  +			s_globalRulesRegistry.Add("property", typeof(PropertyPatternConverter));
   		}
   
   		#endregion Static Constructor
  
  
  
  1.1                  logging-log4net/src/Util/CompositeProperties.cs
  
  Index: CompositeProperties.cs
  ===================================================================
  #region Copyright & License
  //
  // Copyright 2001-2004 The Apache Software Foundation
  //
  // Licensed 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;
  
  namespace log4net.Util
  {
  	/// <summary>
  	/// This class aggregates several PropertiesDictionary collections together.
  	/// </summary>
  	/// <author>Nicko Cadell</author>
  	public sealed class CompositeProperties
  	{
  		#region Private Instance Fields
  
  		private PropertiesDictionary m_flattened = null;
  		private ArrayList m_nestedProperties = new ArrayList();
  
  		#endregion Private Instance Fields
  
  		#region Public Instance Constructors
  
  		/// <summary>
  		/// Initializes a new instance of the <see cref="CompositeProperties" /> class.
  		/// </summary>
  		internal CompositeProperties()
  		{
  		}
  
  		#endregion Public Instance Constructors
  
  		#region Public Instance Properties
  
  		/// <summary>
  		/// Gets the value of a property
  		/// </summary>
  		/// <value>
  		/// The value for the property with the specified key
  		/// </value>
  		public object this[string key]
  		{
  			get 
  			{
  				// Look in the flattened properties first
  				if (m_flattened != null)
  				{
  					return m_flattened[key];
  				}
  
  				// Look for the key in all the nested properties
  				foreach(ReadOnlyPropertiesDictionary cur in m_nestedProperties)
  				{
  					if (cur.Contains(key))
  					{
  						return cur[key];
  					}
  				}
  				return null;
  			}
  		}
  
  		#endregion Public Instance Properties
  
  		#region Public Instance Methods
  
  		/// <summary>
  		/// Add a Properties Dictionary to this composite collection
  		/// </summary>
  		/// <param name="properties">the properties to add</param>
  		/// <remarks>
  		/// <para>
  		/// Properties dictionaries added first take precedence over dictionaries added
  		/// later.
  		/// </para>
  		/// </remarks>
  		public void Add(ReadOnlyPropertiesDictionary properties)
  		{
  			m_flattened = null;
  			m_nestedProperties.Add(properties);
  		}
  
  		/// <summary>
  		/// Flatten this composite collection into a single properties dictionary
  		/// </summary>
  		/// <returns>the flattened dictionary</returns>
  		public PropertiesDictionary Flatten()
  		{
  			if (m_flattened == null)
  			{
  				m_flattened = new PropertiesDictionary();
  
  				for(int i=m_nestedProperties.Count; --i>=0; )
  				{
  					ReadOnlyPropertiesDictionary cur = (ReadOnlyPropertiesDictionary)m_nestedProperties[i];
  
  					foreach(DictionaryEntry entry in cur)
  					{
  						m_flattened[(string)entry.Key] = entry.Value;
  					}
  				}
  			}
  			return m_flattened;
  		}
  
  		#endregion Public Instance Methods
  	}
  }
  
  
  
  
  1.1                  logging-log4net/src/Util/ThreadContextList.cs
  
  Index: ThreadContextList.cs
  ===================================================================
  #region Copyright & License
  //
  // Copyright 2001-2004 The Apache Software Foundation
  //
  // Licensed 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.Text;
  using System.Collections;
  
  using log4net.Core;
  
  namespace log4net.Util
  {
  	/// <summary>
  	/// Implementation of List for the <see cref="log4net.ThreadContext"/>
  	/// </summary>
  	/// <remarks>
  	/// <para>
  	/// </para>
  	/// </remarks>
  	/// <author>Nicko Cadell</author>
  	public sealed class ThreadContextList : IFixingRequired
  	{
  		#region Private Static Fields
  
  		/// <summary>
  		/// The thread local data slot used to store the stack.
  		/// </summary>
  		private readonly ArrayList m_list = new ArrayList();
  
  		#endregion Private Static Fields
  
  		#region Public Instance Constructors
  
  		/// <summary>
  		/// Initializes a new instance of the <see cref="ThreadContextStack" /> class. 
  		/// </summary>
  		internal ThreadContextList()
  		{
  		}
  
  		#endregion Public Instance Constructors
  
  		#region Public Methods
  
  		/// <summary>
  		/// Clears all the contextual information held in this list.
  		/// </summary>
  		public void Clear() 
  		{
  			m_list.Clear();
  		}
  
  		/// <summary>
  		/// Append a message to this list
  		/// </summary>
  		/// <param name="message">the message to append to this list</param>
  		public void Append(string message) 
  		{
  			m_list.Add(message);
  		}
  
  		#endregion Public Methods
  
  		#region Internal Methods
  
  		/// <summary>
  		/// Gets the current context information for this list.
  		/// </summary>
  		/// <returns>The current context information.</returns>
  		internal string GetFullMessage() 
  		{
  			if (m_list.Count > 1)
  			{
  				// Build message using a string builder
  
  				StringBuilder buf = new StringBuilder();
  
  				buf.Append((string)m_list[0]);
  
  				for(int i=1; i<m_list.Count; i++)
  				{
  					buf.Append(' ');
  					buf.Append((string)m_list[i]);
  				}
  
  				return buf.ToString();
  			}
  			else if (m_list.Count == 1)
  			{
  				// Only one element, just return it
  				return (string)m_list[0];
  			}
  			return null;
  		}
    
  		#endregion Internal Methods
  
  		/// <summary>
  		/// Gets the current context information for this list.
  		/// </summary>
  		/// <returns>Gets the current context information</returns>
  		public override string ToString()
  		{
  			return GetFullMessage();
  		}
  
  		/// <summary>
  		/// Get a portable version of this object
  		/// </summary>
  		/// <returns>the portable instance of this object</returns>
  		object IFixingRequired.GetFixedObject()
  		{
  			return GetFullMessage();
  		}
  	}
  }
  
  
  
  1.1                  logging-log4net/src/Util/ThreadContextLists.cs
  
  Index: ThreadContextLists.cs
  ===================================================================
  #region Copyright & License
  //
  // Copyright 2001-2004 The Apache Software Foundation
  //
  // Licensed 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;
  
  namespace log4net.Util
  {
  	/// <summary>
  	/// Implementation of Lists collection for the <see cref="log4net.ThreadContext"/>
  	/// </summary>
  	/// <remarks>
  	/// <para>
  	/// </para>
  	/// </remarks>
  	/// <author>Nicko Cadell</author>
  	public sealed class ThreadContextLists
  	{
  		#region Public Instance Constructors
  
  		/// <summary>
  		/// Initializes a new instance of the <see cref="ThreadContextLists" /> class.
  		/// </summary>
  		internal ThreadContextLists()
  		{
  		}
  
  		#endregion Public Instance Constructors
  
  		#region Public Instance Properties
  
  		/// <summary>
  		/// Gets or sets the value of a property
  		/// </summary>
  		/// <value>
  		/// The value for the property with the specified key
  		/// </value>
  		public ThreadContextList this[string key]
  		{
  			get 
  			{
  				ThreadContextList list = null;
  
  				object propertyValue = ThreadContext.Properties[key];
  				if (propertyValue == null)
  				{
  					// List does not exist, create
  					list = new ThreadContextList();
  					ThreadContext.Properties[key] = list;
  				}
  				else
  				{
  					// Look for existing list
  					list = propertyValue as ThreadContextList;
  					if (list == null)
  					{
  						// Property is not set to a list!
  						string propertyValueString = "(null)";
  
  						try
  						{
  							propertyValueString = propertyValue.ToString();
  						}
  						catch
  						{
  						}
  
  						LogLog.Error("ThreadContextLists: Request for list named ["+key+"] failed because a property with the same name exists which is a ["+propertyValue.GetType().Name+"] with value ["+propertyValueString+"]");
  
  						list = new ThreadContextList();
  					}
  				}
  
  				return list;
  			}
  		}
  
  		#endregion Public Instance Properties
  	}
  }
  
  
  
  
  1.1                  logging-log4net/src/Util/ThreadContextProperties.cs
  
  Index: ThreadContextProperties.cs
  ===================================================================
  #region Copyright & License
  //
  // Copyright 2001-2004 The Apache Software Foundation
  //
  // Licensed 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;
  
  namespace log4net.Util
  {
  	/// <summary>
  	/// Implementation of Properties collection for the <see cref="log4net.ThreadContext"/>
  	/// </summary>
  	/// <remarks>
  	/// <para>
  	/// Class implements a collection of properties that is specific to each thread.
  	/// The class is not synchronized as each thread has its own <see cref="PropertiesDictionary"/>.
  	/// </para>
  	/// </remarks>
  	/// <author>Nicko Cadell</author>
  	public sealed class ThreadContextProperties
  	{
  		#region Private Instance Fields
  
  		/// <summary>
  		/// The thread local data slot to use to store a PropertiesDictionary.
  		/// </summary>
  		private readonly static LocalDataStoreSlot s_threadLocalSlot = System.Threading.Thread.AllocateDataSlot();
  
  		#endregion Private Instance Fields
  
  		#region Public Instance Constructors
  
  		/// <summary>
  		/// Initializes a new instance of the <see cref="ThreadContextProperties" /> class.
  		/// </summary>
  		internal ThreadContextProperties()
  		{
  		}
  
  		#endregion Public Instance Constructors
  
  		#region Public Instance Properties
  
  		/// <summary>
  		/// Gets or sets the value of a property
  		/// </summary>
  		/// <value>
  		/// The value for the property with the specified key
  		/// </value>
  		public object this[string key]
  		{
  			get { return GetProperties()[key]; }
  			set { GetProperties()[key] = value; }
  		}
  
  		#endregion Public Instance Properties
  
  		#region Public Instance Methods
  
  		/// <summary>
  		/// Remove a property
  		/// </summary>
  		/// <param name="key">the key for the entry to remove</param>
  		public void Remove(string key)
  		{
  			GetProperties().Remove(key);
  		}
  
  		/// <summary>
  		/// Clear the global context properties
  		/// </summary>
  		public void Clear()
  		{
  			GetProperties().Clear();
  		}
  
  		#endregion Public Instance Methods
  
  		#region Internal Instance Methods
  
  		/// <summary>
  		/// Get the PropertiesDictionary stored in the LocalDataStoreSlot for this thread.
  		/// </summary>
  		/// <returns>the properties for this thread</returns>
  		/// <remarks>
  		/// <para>
  		/// The collection returned is only to be used on the calling thread. If the
  		/// caller needs to share the collection between different threads then the 
  		/// caller must clone the collection before doings so.
  		/// </para>
  		/// </remarks>
  		internal PropertiesDictionary GetProperties()
  		{
  			PropertiesDictionary properties = (PropertiesDictionary)System.Threading.Thread.GetData(s_threadLocalSlot);
  			if (properties == null)
  			{
  				properties  = new PropertiesDictionary();
  				System.Threading.Thread.SetData(s_threadLocalSlot, properties);
  			}
  			return properties;
  		}
  
  		#endregion Internal Instance Methods
  	}
  }
  
  
  
  
  1.1                  logging-log4net/src/Util/ThreadContextStack.cs
  
  Index: ThreadContextStack.cs
  ===================================================================
  #region Copyright & License
  //
  // Copyright 2001-2004 The Apache Software Foundation
  //
  // Licensed 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;
  
  #if !NETCF
  using System.Collections;
  #endif
  
  using log4net.Core;
  
  namespace log4net.Util
  {
  	/// <summary>
  	/// Implementation of Stack for the <see cref="log4net.ThreadContext"/>
  	/// </summary>
  	/// <remarks>
  	/// <para>
  	/// </para>
  	/// </remarks>
  	/// <author>Nicko Cadell</author>
  	public sealed class ThreadContextStack : IFixingRequired
  	{
  		#region Private Static Fields
  
  		/// <summary>
  		/// The stack store.
  		/// </summary>
  		private readonly Stack m_stack = new Stack();
  
  		#endregion Private Static Fields
  
  		#region Public Instance Constructors
  
  		/// <summary>
  		/// Initializes a new instance of the <see cref="ThreadContextStack" /> class. 
  		/// </summary>
  		internal ThreadContextStack()
  		{
  		}
  
  		#endregion Public Instance Constructors
  
  		#region Public Methods
  
  //		/// <summary>
  //		/// Clears all the contextual information held in this stack.
  //		/// </summary>
  //		public void Clear() 
  //		{
  //			m_stack.Clear();
  //		}
  
  		/// <summary>
  		/// Removes the top context from this stack.
  		/// </summary>
  		/// <remarks>
  		/// Remove the top context from this stack, and return
  		/// it to the caller. If this stack is empty then an
  		/// empty string (not <see langword="null"/>) is returned.
  		/// </remarks>
  		/// <returns>
  		/// The message in the context that was removed from the top 
  		/// of this stack.
  		/// </returns>
  		public string Pop() 
  		{
  			Stack stack = m_stack;
  			if (stack.Count > 0)
  			{
  				return ((StackFrame)(stack.Pop())).Message;
  			}
  			return "";
  		}
  
  		/// <summary>
  		/// Pushes a new context message into this stack.
  		/// </summary>
  		/// <param name="message">The new context message.</param>
  		/// <returns>
  		/// An <see cref="IDisposable"/> that can be used to clean up the context stack.
  		/// </returns>
  		/// <remarks>
  		/// Pushes a new context onto this stack. An <see cref="IDisposable"/>
  		/// is returned that can be used to clean up this stack. This
  		/// can be easily combined with the <c>using</c> keyword to scope the
  		/// context.
  		/// </remarks>
  		/// <example>Simple example of using the <c>Push</c> method with the <c>using</c> keyword.
  		/// <code>
  		/// using(log4net.ThreadContext.Stacks["NDC"].Push("Stack_Message"))
  		/// {
  		///		log.Warn("This should have an ThreadContext Stack message");
  		///	}
  		/// </code>
  		/// </example>
  		public IDisposable Push(string message) 
  		{
  			Stack stack = m_stack;
  			stack.Push(new StackFrame(message, (stack.Count>0) ? (StackFrame)stack.Peek() : null));
  
  			return new AutoPopStackFrame(stack, stack.Count - 1);
  		}
  
  		#endregion Public Methods
  
  		#region Internal Methods
  
  		/// <summary>
  		/// Gets the current context information for this stack.
  		/// </summary>
  		/// <returns>The current context information.</returns>
  		internal string GetFullMessage() 
  		{
  			Stack stack = m_stack;
  			if (stack.Count > 0)
  			{
  				return ((StackFrame)(stack.Peek())).FullMessage;
  			}
  			return null;
  		}
    
  		#endregion Internal Methods
  
  		/// <summary>
  		/// Gets the current context information  for this stack.
  		/// </summary>
  		/// <returns>Gets the current context information</returns>
  		public override string ToString()
  		{
  			return GetFullMessage();
  		}
  
  		/// <summary>
  		/// Get a portable version of this object
  		/// </summary>
  		/// <returns>the portable instance of this object</returns>
  		object IFixingRequired.GetFixedObject()
  		{
  			return GetFullMessage();
  		}
  
  		/// <summary>
  		/// Inner class used to represent a single context frame in the stack.
  		/// </summary>
  		private sealed class StackFrame 
  		{
  			#region Private Instance Fields
  
  			private readonly string m_message;
  			private readonly StackFrame m_parent;
  			private string m_fullMessage = null;
      
  			#endregion
  
  			#region Internal Instance Constructors
  
  			/// <summary>
  			/// Initializes a new instance of the <see cref="StackFrame" /> class
  			/// with the specified message and parent context.
  			/// </summary>
  			/// <param name="message">The message for this context.</param>
  			/// <param name="parent">The parent context in the chain.</param>
  			internal StackFrame(string message, StackFrame parent) 
  			{
  				m_message = message;
  				m_parent = parent;
  
  				if (parent == null) 
  				{
  					m_fullMessage = message;
  				} 
  			}
  
  			#endregion Internal Instance Constructors
  
  			#region Internal Instance Properties
  
  			/// <summary>
  			/// Get the message.
  			/// </summary>
  			/// <value>The message.</value>
  			internal string Message
  			{
  				get { return m_message; }
  			}
  
  			/// <summary>
  			/// Gets the full text of the context down to the root level.
  			/// </summary>
  			/// <value>
  			/// The full text of the context down to the root level.
  			/// </value>
  			internal string FullMessage
  			{
  				get 
  				{
  					if (m_fullMessage == null)
  					{
  						m_fullMessage = string.Concat(m_parent.FullMessage, " ", m_message);
  					}
  					return m_fullMessage; 
  				}
  			}
  
  			#endregion Internal Instance Properties
  		}
  
  		/// <summary>
  		/// Struct returned from the <see cref="ThreadContextStack.Push"/> method.
  		/// </summary>
  		/// <remarks>
  		/// This struct implements the <see cref="IDisposable"/> and is designed to be used
  		/// with the <see langword="using"/> pattern to remove the stack frame at the end of the scope.
  		/// </remarks>
  		private struct AutoPopStackFrame : IDisposable
  		{
  			#region Private Instance Fields
  
  			/// <summary>
  			/// The ThreadContextStack internal stack
  			/// </summary>
  			private Stack m_frameStack;
  
  			/// <summary>
  			/// The depth to trim the stack to when this instance is disposed
  			/// </summary>
  			private int m_frameDepth;
  
  			#endregion Private Instance Fields
  
  			#region Internal Instance Constructors
  
  			/// <summary>
  			/// Initializes a new instance of the <see cref="AutoPopStackFrame" /> class with
  			/// the specified stack and return depth.
  			/// </summary>
  			/// <param name="frameStack">The internal stack used by the ThreadContextStack.</param>
  			/// <param name="frameDepth">The depth to return the stack to when this object is disposed.</param>
  			internal AutoPopStackFrame(Stack frameStack, int frameDepth)
  			{
  				m_frameStack = frameStack;
  				m_frameDepth = frameDepth;
  			}
  
  			#endregion Internal Instance Constructors
  
  			#region Implementation of IDisposable
  
  			/// <summary>
  			/// Returns the stack to the correct depth.
  			/// </summary>
  			public void Dispose()
  			{
  				if (m_frameDepth >= 0 && m_frameStack != null)
  				{
  					while(m_frameStack.Count > m_frameDepth)
  					{
  						m_frameStack.Pop();
  					}
  				}
  			}
  
  			#endregion Implementation of IDisposable
  		}
  
  #if NETCF
  		/// <summary>
  		/// Subclass of <see cref="System.Collections.Stack"/> to
  		/// provide missing methods.
  		/// </summary>
  		/// <remarks>
  		/// The Compact Framework version of the <see cref="System.Collections.Stack"/>
  		/// class is missing the <c>Clear</c> and <c>Clone</c> methods.
  		/// This subclass adds implementations of those missing methods.
  		/// </remarks>
  		public class Stack : System.Collections.Stack
  		{
  			/// <summary>
  			/// Clears the stack of all elements.
  			/// </summary>
  			public void Clear()
  			{
  				while(Count > 0)
  				{
  					Pop();
  				}
  			}
  
  			/// <summary>
  			/// Makes a shallow copy of the stack's elements.
  			/// </summary>
  			/// <returns>A new stack that has a shallow copy of the stack's elements.</returns>
  			public Stack Clone()
  			{
  				Stack res = new Stack();
  				object[] items = ToArray();
  				foreach(object item in items)
  				{
  					res.Push(item);
  				}
  				return res;
  			}
  		}
  #endif
  	}
  }
  
  
  
  1.1                  logging-log4net/src/Util/ThreadContextStacks.cs
  
  Index: ThreadContextStacks.cs
  ===================================================================
  #region Copyright & License
  //
  // Copyright 2001-2004 The Apache Software Foundation
  //
  // Licensed 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;
  
  namespace log4net.Util
  {
  	/// <summary>
  	/// Implementation of Stacks collection for the <see cref="log4net.ThreadContext"/>
  	/// </summary>
  	/// <remarks>
  	/// <para>
  	/// </para>
  	/// </remarks>
  	/// <author>Nicko Cadell</author>
  	public sealed class ThreadContextStacks
  	{
  		#region Public Instance Constructors
  
  		/// <summary>
  		/// Initializes a new instance of the <see cref="ThreadContextStacks" /> class.
  		/// </summary>
  		internal ThreadContextStacks()
  		{
  		}
  
  		#endregion Public Instance Constructors
  
  		#region Public Instance Properties
  
  		/// <summary>
  		/// Gets or sets the value of a property
  		/// </summary>
  		/// <value>
  		/// The value for the property with the specified key
  		/// </value>
  		public ThreadContextStack this[string key]
  		{
  			get 
  			{
  				ThreadContextStack stack = null;
  
  				object propertyValue = ThreadContext.Properties[key];
  				if (propertyValue == null)
  				{
  					// Stack does not exist, create
  					stack = new ThreadContextStack();
  					ThreadContext.Properties[key] = stack;
  				}
  				else
  				{
  					// Look for existing stack
  					stack = propertyValue as ThreadContextStack;
  					if (stack == null)
  					{
  						// Property is not set to a stack!
  						string propertyValueString = "(null)";
  
  						try
  						{
  							propertyValueString = propertyValue.ToString();
  						}
  						catch
  						{
  						}
  
  						LogLog.Error("ThreadContextStacks: Request for stack named ["+key+"] failed because a property with the same name exists which is a ["+propertyValue.GetType().Name+"] with value ["+propertyValueString+"]");
  
  						stack = new ThreadContextStack();
  					}
  				}
  
  				return stack;
  			}
  		}
  
  		#endregion Public Instance Properties
  	}
  }
  
  
  
  
  1.1                  logging-log4net/src/Util/PatternStringConverters/PropertyPatternConverter.cs
  
  Index: PropertyPatternConverter.cs
  ===================================================================
  #region Copyright & License
  //
  // Copyright 2001-2004 The Apache Software Foundation
  //
  // Licensed 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.Text;
  using System.IO;
  using System.Collections;
  
  using log4net.Core;
  using log4net.Util;
  using log4net.Repository;
  
  namespace log4net.Util.PatternStringConverters
  {
  	/// <summary>
  	/// Property pattern converter
  	/// </summary>
  	/// <remarks>
  	/// <para>
  	/// This pattern converter reads the thread and global properties.
  	/// The thread properties take priority over global properties.
  	/// See <see cref="ThreadContext.Properties"/> for details of the 
  	/// thread properties. See <see cref="GlobalContext.Properties"/> for
  	/// details of the global properties.
  	/// </para>
  	/// <para>
  	/// If the <see cref="PatternConverter.Option"/> is specified then that will be used to
  	/// lookup a single property. If no <see cref="PatternConverter.Option"/> is specified
  	/// then all properties will be dumped as a list of key value pairs.
  	/// </para>
  	/// </remarks>
  	/// <author>Nicko Cadell</author>
  	internal sealed class PropertyPatternConverter : PatternConverter 
  	{
  		/// <summary>
  		/// Write the property or specified properties as required
  		/// </summary>
  		/// <param name="writer"><see cref="TextWriter" /> that will receive the formatted result.</param>
  		/// <param name="state">reserved</param>
  		/// <returns>the result of converting the pattern</returns>
  		override protected void Convert(TextWriter writer, object state) 
  		{
  			CompositeProperties compositeProperties = new CompositeProperties();
  
  			compositeProperties.Add(ThreadContext.Properties.GetProperties());
  			// TODO: Add Repository Properties
  			compositeProperties.Add(GlobalContext.Properties.GetReadOnlyProperties());
  
  			if (Option != null)
  			{
  				// Write the value for the specified key
  				WriteObject(writer, null, compositeProperties[Option]);
  			}
  			else
  			{
  				// Write all the key value pairs
  				WriteDictionary(writer, null, compositeProperties.Flatten());
  			}
  		}
  	}
  }