#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