#region Apache License // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to you under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #endregion #if !NETCF using System; using System.Collections; using log4net.Core; namespace log4net.Util { /// /// Delegate type used for LogicalThreadContextStack's callbacks. /// #if NET_2_0 || MONO_2_0 || NETSTANDARD public delegate void TwoArgAction(T1 t1, T2 t2); #else public delegate void TwoArgAction(string t1, LogicalThreadContextStack t2); #endif /// /// Implementation of Stack for the /// /// /// /// Implementation of Stack for the /// /// /// Nicko Cadell public sealed class LogicalThreadContextStack : IFixingRequired { #region Private Instance Fields /// /// The stack store. /// private Stack m_stack = new Stack(); /// /// The name of this within the /// . /// private string m_propertyKey; /// /// The callback used to let the register a /// new instance of a . /// #if NET_2_0 || MONO_2_0 || NETSTANDARD private TwoArgAction m_registerNew; #else private TwoArgAction m_registerNew; #endif #endregion Private Instance Fields #region Public Instance Constructors /// /// Internal constructor /// /// /// /// Initializes a new instance of the class. /// /// #if NET_2_0 || MONO_2_0 || NETSTANDARD internal LogicalThreadContextStack(string propertyKey, TwoArgAction registerNew) #else internal LogicalThreadContextStack(string propertyKey, TwoArgAction registerNew) #endif { m_propertyKey = propertyKey; m_registerNew = registerNew; } #endregion Public Instance Constructors #region Public Properties /// /// The number of messages in the stack /// /// /// The current number of messages in the stack /// /// /// /// The current number of messages in the stack. That is /// the number of times has been called /// minus the number of times has been called. /// /// public int Count { get { return m_stack.Count; } } #endregion // Public Properties #region Public Methods /// /// Clears all the contextual information held in this stack. /// /// /// /// Clears all the contextual information held in this stack. /// Only call this if you think that this thread is being reused after /// a previous call execution which may not have completed correctly. /// You do not need to use this method if you always guarantee to call /// the method of the /// returned from even in exceptional circumstances, /// for example by using the using(log4net.LogicalThreadContext.Stacks["NDC"].Push("Stack_Message")) /// syntax. /// /// public void Clear() { m_registerNew(m_propertyKey, new LogicalThreadContextStack(m_propertyKey, m_registerNew)); } /// /// Removes the top context from this stack. /// /// The message in the context that was removed from the top of this stack. /// /// /// Remove the top context from this stack, and return /// it to the caller. If this stack is empty then an /// empty string (not ) is returned. /// /// public string Pop() { // copy current stack Stack stack = new Stack(new Stack(m_stack)); string result = ""; if (stack.Count > 0) { result = ((StackFrame)(stack.Pop())).Message; } LogicalThreadContextStack ltcs = new LogicalThreadContextStack(m_propertyKey, m_registerNew); ltcs.m_stack = stack; m_registerNew(m_propertyKey, ltcs); return result; } /// /// Pushes a new context message into this stack. /// /// The new context message. /// /// An that can be used to clean up the context stack. /// /// /// /// Pushes a new context onto this stack. An /// is returned that can be used to clean up this stack. This /// can be easily combined with the using keyword to scope the /// context. /// /// /// Simple example of using the Push method with the using keyword. /// /// using(log4net.LogicalThreadContext.Stacks["NDC"].Push("Stack_Message")) /// { /// log.Warn("This should have an ThreadContext Stack message"); /// } /// /// public IDisposable Push(string message) { // do modifications on a copy Stack stack = new Stack(new Stack(m_stack)); stack.Push(new StackFrame(message, (stack.Count > 0) ? (StackFrame)stack.Peek() : null)); LogicalThreadContextStack contextStack = new LogicalThreadContextStack(m_propertyKey, m_registerNew); contextStack.m_stack = stack; m_registerNew(m_propertyKey, contextStack); return new AutoPopStackFrame(contextStack, stack.Count - 1); } #endregion Public Methods #region Internal Methods /// /// Gets the current context information for this stack. /// /// The current context information. internal string GetFullMessage() { Stack stack = m_stack; if (stack.Count > 0) { return ((StackFrame)(stack.Peek())).FullMessage; } return null; } /// /// Gets and sets the internal stack used by this /// /// The internal storage stack /// /// /// This property is provided only to support backward compatability /// of the . Tytpically the internal stack should not /// be modified. /// /// internal Stack InternalStack { get { return m_stack; } set { m_stack = value; } } #endregion Internal Methods /// /// Gets the current context information for this stack. /// /// Gets the current context information /// /// /// Gets the current context information for this stack. /// /// public override string ToString() { return GetFullMessage(); } /// /// Get a portable version of this object /// /// the portable instance of this object /// /// /// Get a cross thread portable version of this object /// /// object IFixingRequired.GetFixedObject() { return GetFullMessage(); } /// /// Inner class used to represent a single context frame in the stack. /// /// /// /// Inner class used to represent a single context frame in the stack. /// /// 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 /// /// Constructor /// /// The message for this context. /// The parent context in the chain. /// /// /// Initializes a new instance of the class /// with the specified message and parent context. /// /// 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 /// /// Get the message. /// /// The message. /// /// /// Get the message. /// /// internal string Message { get { return m_message; } } /// /// Gets the full text of the context down to the root level. /// /// /// The full text of the context down to the root level. /// /// /// /// Gets the full text of the context down to the root level. /// /// internal string FullMessage { get { if (m_fullMessage == null && m_parent != null) { m_fullMessage = string.Concat(m_parent.FullMessage, " ", m_message); } return m_fullMessage; } } #endregion Internal Instance Properties } /// /// Struct returned from the method. /// /// /// /// This struct implements the and is designed to be used /// with the pattern to remove the stack frame at the end of the scope. /// /// private struct AutoPopStackFrame : IDisposable { #region Private Instance Fields /// /// The depth to trim the stack to when this instance is disposed /// private int m_frameDepth; /// /// The outer LogicalThreadContextStack. /// private LogicalThreadContextStack m_logicalThreadContextStack; #endregion Private Instance Fields #region Internal Instance Constructors /// /// Constructor /// /// The internal stack used by the ThreadContextStack. /// The depth to return the stack to when this object is disposed. /// /// /// Initializes a new instance of the class with /// the specified stack and return depth. /// /// internal AutoPopStackFrame(LogicalThreadContextStack logicalThreadContextStack, int frameDepth) { m_frameDepth = frameDepth; m_logicalThreadContextStack = logicalThreadContextStack; } #endregion Internal Instance Constructors #region Implementation of IDisposable /// /// Returns the stack to the correct depth. /// /// /// /// Returns the stack to the correct depth. /// /// public void Dispose() { if (m_frameDepth >= 0 && m_logicalThreadContextStack.m_stack != null) { Stack stack = new Stack(new Stack(m_logicalThreadContextStack.m_stack)); while (stack.Count > m_frameDepth) { stack.Pop(); } LogicalThreadContextStack ltcs = new LogicalThreadContextStack(m_logicalThreadContextStack.m_propertyKey, m_logicalThreadContextStack.m_registerNew); ltcs.m_stack = stack; m_logicalThreadContextStack.m_registerNew(m_logicalThreadContextStack.m_propertyKey, ltcs); } } #endregion Implementation of IDisposable } } } #endif