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());
}
}
}
}