LogicalThreadContextStack.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. #region Apache License
  2. //
  3. // Licensed to the Apache Software Foundation (ASF) under one or more
  4. // contributor license agreements. See the NOTICE file distributed with
  5. // this work for additional information regarding copyright ownership.
  6. // The ASF licenses this file to you under the Apache License, Version 2.0
  7. // (the "License"); you may not use this file except in compliance with
  8. // the License. You may obtain a copy of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. //
  18. #endregion
  19. #if !NETCF
  20. using System;
  21. using System.Collections;
  22. using log4net.Core;
  23. namespace log4net.Util
  24. {
  25. /// <summary>
  26. /// Delegate type used for LogicalThreadContextStack's callbacks.
  27. /// </summary>
  28. #if NET_2_0 || MONO_2_0
  29. public delegate void TwoArgAction<T1, T2>(T1 t1, T2 t2);
  30. #else
  31. public delegate void TwoArgAction(string t1, LogicalThreadContextStack t2);
  32. #endif
  33. /// <summary>
  34. /// Implementation of Stack for the <see cref="log4net.LogicalThreadContext"/>
  35. /// </summary>
  36. /// <remarks>
  37. /// <para>
  38. /// Implementation of Stack for the <see cref="log4net.LogicalThreadContext"/>
  39. /// </para>
  40. /// </remarks>
  41. /// <author>Nicko Cadell</author>
  42. public sealed class LogicalThreadContextStack : IFixingRequired
  43. {
  44. #region Private Instance Fields
  45. /// <summary>
  46. /// The stack store.
  47. /// </summary>
  48. private Stack m_stack = new Stack();
  49. /// <summary>
  50. /// The name of this <see cref="log4net.Util.LogicalThreadContextStack"/> within the
  51. /// <see cref="log4net.Util.LogicalThreadContextProperties"/>.
  52. /// </summary>
  53. private string m_propertyKey;
  54. /// <summary>
  55. /// The callback used to let the <see cref="log4net.Util.LogicalThreadContextStacks"/> register a
  56. /// new instance of a <see cref="log4net.Util.LogicalThreadContextStack"/>.
  57. /// </summary>
  58. #if NET_2_0 || MONO_2_0
  59. private TwoArgAction<string, LogicalThreadContextStack> m_registerNew;
  60. #else
  61. private TwoArgAction m_registerNew;
  62. #endif
  63. #endregion Private Instance Fields
  64. #region Public Instance Constructors
  65. /// <summary>
  66. /// Internal constructor
  67. /// </summary>
  68. /// <remarks>
  69. /// <para>
  70. /// Initializes a new instance of the <see cref="LogicalThreadContextStack" /> class.
  71. /// </para>
  72. /// </remarks>
  73. #if NET_2_0 || MONO_2_0
  74. internal LogicalThreadContextStack(string propertyKey, TwoArgAction<string, LogicalThreadContextStack> registerNew)
  75. #else
  76. internal LogicalThreadContextStack(string propertyKey, TwoArgAction registerNew)
  77. #endif
  78. {
  79. m_propertyKey = propertyKey;
  80. m_registerNew = registerNew;
  81. }
  82. #endregion Public Instance Constructors
  83. #region Public Properties
  84. /// <summary>
  85. /// The number of messages in the stack
  86. /// </summary>
  87. /// <value>
  88. /// The current number of messages in the stack
  89. /// </value>
  90. /// <remarks>
  91. /// <para>
  92. /// The current number of messages in the stack. That is
  93. /// the number of times <see cref="Push"/> has been called
  94. /// minus the number of times <see cref="Pop"/> has been called.
  95. /// </para>
  96. /// </remarks>
  97. public int Count
  98. {
  99. get { return m_stack.Count; }
  100. }
  101. #endregion // Public Properties
  102. #region Public Methods
  103. /// <summary>
  104. /// Clears all the contextual information held in this stack.
  105. /// </summary>
  106. /// <remarks>
  107. /// <para>
  108. /// Clears all the contextual information held in this stack.
  109. /// Only call this if you think that this thread is being reused after
  110. /// a previous call execution which may not have completed correctly.
  111. /// You do not need to use this method if you always guarantee to call
  112. /// the <see cref="IDisposable.Dispose"/> method of the <see cref="IDisposable"/>
  113. /// returned from <see cref="Push"/> even in exceptional circumstances,
  114. /// for example by using the <c>using(log4net.LogicalThreadContext.Stacks["NDC"].Push("Stack_Message"))</c>
  115. /// syntax.
  116. /// </para>
  117. /// </remarks>
  118. public void Clear()
  119. {
  120. m_registerNew(m_propertyKey, new LogicalThreadContextStack(m_propertyKey, m_registerNew));
  121. }
  122. /// <summary>
  123. /// Removes the top context from this stack.
  124. /// </summary>
  125. /// <returns>The message in the context that was removed from the top of this stack.</returns>
  126. /// <remarks>
  127. /// <para>
  128. /// Remove the top context from this stack, and return
  129. /// it to the caller. If this stack is empty then an
  130. /// empty string (not <see langword="null"/>) is returned.
  131. /// </para>
  132. /// </remarks>
  133. public string Pop()
  134. {
  135. // copy current stack
  136. Stack stack = new Stack(new Stack(m_stack));
  137. string result = "";
  138. if (stack.Count > 0)
  139. {
  140. result = ((StackFrame)(stack.Pop())).Message;
  141. }
  142. LogicalThreadContextStack ltcs = new LogicalThreadContextStack(m_propertyKey, m_registerNew);
  143. ltcs.m_stack = stack;
  144. m_registerNew(m_propertyKey, ltcs);
  145. return result;
  146. }
  147. /// <summary>
  148. /// Pushes a new context message into this stack.
  149. /// </summary>
  150. /// <param name="message">The new context message.</param>
  151. /// <returns>
  152. /// An <see cref="IDisposable"/> that can be used to clean up the context stack.
  153. /// </returns>
  154. /// <remarks>
  155. /// <para>
  156. /// Pushes a new context onto this stack. An <see cref="IDisposable"/>
  157. /// is returned that can be used to clean up this stack. This
  158. /// can be easily combined with the <c>using</c> keyword to scope the
  159. /// context.
  160. /// </para>
  161. /// </remarks>
  162. /// <example>Simple example of using the <c>Push</c> method with the <c>using</c> keyword.
  163. /// <code lang="C#">
  164. /// using(log4net.LogicalThreadContext.Stacks["NDC"].Push("Stack_Message"))
  165. /// {
  166. /// log.Warn("This should have an ThreadContext Stack message");
  167. /// }
  168. /// </code>
  169. /// </example>
  170. public IDisposable Push(string message)
  171. {
  172. // do modifications on a copy
  173. Stack stack = new Stack(new Stack(m_stack));
  174. stack.Push(new StackFrame(message, (stack.Count > 0) ? (StackFrame)stack.Peek() : null));
  175. LogicalThreadContextStack contextStack = new LogicalThreadContextStack(m_propertyKey, m_registerNew);
  176. contextStack.m_stack = stack;
  177. m_registerNew(m_propertyKey, contextStack);
  178. return new AutoPopStackFrame(contextStack, stack.Count - 1);
  179. }
  180. #endregion Public Methods
  181. #region Internal Methods
  182. /// <summary>
  183. /// Gets the current context information for this stack.
  184. /// </summary>
  185. /// <returns>The current context information.</returns>
  186. internal string GetFullMessage()
  187. {
  188. Stack stack = m_stack;
  189. if (stack.Count > 0)
  190. {
  191. return ((StackFrame)(stack.Peek())).FullMessage;
  192. }
  193. return null;
  194. }
  195. /// <summary>
  196. /// Gets and sets the internal stack used by this <see cref="LogicalThreadContextStack"/>
  197. /// </summary>
  198. /// <value>The internal storage stack</value>
  199. /// <remarks>
  200. /// <para>
  201. /// This property is provided only to support backward compatability
  202. /// of the <see cref="NDC"/>. Tytpically the internal stack should not
  203. /// be modified.
  204. /// </para>
  205. /// </remarks>
  206. internal Stack InternalStack
  207. {
  208. get { return m_stack; }
  209. set { m_stack = value; }
  210. }
  211. #endregion Internal Methods
  212. /// <summary>
  213. /// Gets the current context information for this stack.
  214. /// </summary>
  215. /// <returns>Gets the current context information</returns>
  216. /// <remarks>
  217. /// <para>
  218. /// Gets the current context information for this stack.
  219. /// </para>
  220. /// </remarks>
  221. public override string ToString()
  222. {
  223. return GetFullMessage();
  224. }
  225. /// <summary>
  226. /// Get a portable version of this object
  227. /// </summary>
  228. /// <returns>the portable instance of this object</returns>
  229. /// <remarks>
  230. /// <para>
  231. /// Get a cross thread portable version of this object
  232. /// </para>
  233. /// </remarks>
  234. object IFixingRequired.GetFixedObject()
  235. {
  236. return GetFullMessage();
  237. }
  238. /// <summary>
  239. /// Inner class used to represent a single context frame in the stack.
  240. /// </summary>
  241. /// <remarks>
  242. /// <para>
  243. /// Inner class used to represent a single context frame in the stack.
  244. /// </para>
  245. /// </remarks>
  246. private sealed class StackFrame
  247. {
  248. #region Private Instance Fields
  249. private readonly string m_message;
  250. private readonly StackFrame m_parent;
  251. private string m_fullMessage = null;
  252. #endregion
  253. #region Internal Instance Constructors
  254. /// <summary>
  255. /// Constructor
  256. /// </summary>
  257. /// <param name="message">The message for this context.</param>
  258. /// <param name="parent">The parent context in the chain.</param>
  259. /// <remarks>
  260. /// <para>
  261. /// Initializes a new instance of the <see cref="StackFrame" /> class
  262. /// with the specified message and parent context.
  263. /// </para>
  264. /// </remarks>
  265. internal StackFrame(string message, StackFrame parent)
  266. {
  267. m_message = message;
  268. m_parent = parent;
  269. if (parent == null)
  270. {
  271. m_fullMessage = message;
  272. }
  273. }
  274. #endregion Internal Instance Constructors
  275. #region Internal Instance Properties
  276. /// <summary>
  277. /// Get the message.
  278. /// </summary>
  279. /// <value>The message.</value>
  280. /// <remarks>
  281. /// <para>
  282. /// Get the message.
  283. /// </para>
  284. /// </remarks>
  285. internal string Message
  286. {
  287. get { return m_message; }
  288. }
  289. /// <summary>
  290. /// Gets the full text of the context down to the root level.
  291. /// </summary>
  292. /// <value>
  293. /// The full text of the context down to the root level.
  294. /// </value>
  295. /// <remarks>
  296. /// <para>
  297. /// Gets the full text of the context down to the root level.
  298. /// </para>
  299. /// </remarks>
  300. internal string FullMessage
  301. {
  302. get
  303. {
  304. if (m_fullMessage == null && m_parent != null)
  305. {
  306. m_fullMessage = string.Concat(m_parent.FullMessage, " ", m_message);
  307. }
  308. return m_fullMessage;
  309. }
  310. }
  311. #endregion Internal Instance Properties
  312. }
  313. /// <summary>
  314. /// Struct returned from the <see cref="LogicalThreadContextStack.Push"/> method.
  315. /// </summary>
  316. /// <remarks>
  317. /// <para>
  318. /// This struct implements the <see cref="IDisposable"/> and is designed to be used
  319. /// with the <see langword="using"/> pattern to remove the stack frame at the end of the scope.
  320. /// </para>
  321. /// </remarks>
  322. private struct AutoPopStackFrame : IDisposable
  323. {
  324. #region Private Instance Fields
  325. /// <summary>
  326. /// The depth to trim the stack to when this instance is disposed
  327. /// </summary>
  328. private int m_frameDepth;
  329. /// <summary>
  330. /// The outer LogicalThreadContextStack.
  331. /// </summary>
  332. private LogicalThreadContextStack m_logicalThreadContextStack;
  333. #endregion Private Instance Fields
  334. #region Internal Instance Constructors
  335. /// <summary>
  336. /// Constructor
  337. /// </summary>
  338. /// <param name="logicalThreadContextStack">The internal stack used by the ThreadContextStack.</param>
  339. /// <param name="frameDepth">The depth to return the stack to when this object is disposed.</param>
  340. /// <remarks>
  341. /// <para>
  342. /// Initializes a new instance of the <see cref="AutoPopStackFrame" /> class with
  343. /// the specified stack and return depth.
  344. /// </para>
  345. /// </remarks>
  346. internal AutoPopStackFrame(LogicalThreadContextStack logicalThreadContextStack, int frameDepth)
  347. {
  348. m_frameDepth = frameDepth;
  349. m_logicalThreadContextStack = logicalThreadContextStack;
  350. }
  351. #endregion Internal Instance Constructors
  352. #region Implementation of IDisposable
  353. /// <summary>
  354. /// Returns the stack to the correct depth.
  355. /// </summary>
  356. /// <remarks>
  357. /// <para>
  358. /// Returns the stack to the correct depth.
  359. /// </para>
  360. /// </remarks>
  361. public void Dispose()
  362. {
  363. if (m_frameDepth >= 0 && m_logicalThreadContextStack.m_stack != null)
  364. {
  365. Stack stack = new Stack(new Stack(m_logicalThreadContextStack.m_stack));
  366. while (stack.Count > m_frameDepth)
  367. {
  368. stack.Pop();
  369. }
  370. LogicalThreadContextStack ltcs = new LogicalThreadContextStack(m_logicalThreadContextStack.m_propertyKey, m_logicalThreadContextStack.m_registerNew);
  371. ltcs.m_stack = stack;
  372. m_logicalThreadContextStack.m_registerNew(m_logicalThreadContextStack.m_propertyKey,
  373. ltcs);
  374. }
  375. }
  376. #endregion Implementation of IDisposable
  377. }
  378. }
  379. }
  380. #endif