You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

332 lines
12 KiB

  1. // -*- C++ -*-
  2. // Module: Log4CPLUS
  3. // File: ndc.h
  4. // Created: 6/2001
  5. // Author: Tad E. Smith
  6. //
  7. //
  8. // Copyright 2001-2010 Tad E. Smith
  9. //
  10. // Licensed under the Apache License, Version 2.0 (the "License");
  11. // you may not use this file except in compliance with the License.
  12. // You may obtain a copy of the License at
  13. //
  14. // http://www.apache.org/licenses/LICENSE-2.0
  15. //
  16. // Unless required by applicable law or agreed to in writing, software
  17. // distributed under the License is distributed on an "AS IS" BASIS,
  18. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  19. // See the License for the specific language governing permissions and
  20. // limitations under the License.
  21. /** @file
  22. * This header defined the NDC class.
  23. */
  24. #ifndef _LO4CPLUS_NDC_HEADER_
  25. #define _LO4CPLUS_NDC_HEADER_
  26. #include <log4cplus/config.hxx>
  27. #if defined (LOG4CPLUS_HAVE_PRAGMA_ONCE)
  28. #pragma once
  29. #endif
  30. #include <log4cplus/tstring.h>
  31. #include <map>
  32. #include <deque>
  33. namespace log4cplus {
  34. // Forward declarations
  35. struct DiagnosticContext;
  36. typedef std::deque<DiagnosticContext> DiagnosticContextStack;
  37. /**
  38. * The NDC class implements <i>nested diagnostic contexts</i> as
  39. * defined by Neil Harrison in the article "Patterns for Logging
  40. * Diagnostic Messages" part of the book "<i>Pattern Languages of
  41. * Program Design 3</i>" edited by Martin et al.
  42. *
  43. * A Nested Diagnostic Context, or NDC in short, is an instrument
  44. * to distinguish interleaved log output from different sources. Log
  45. * output is typically interleaved when a server handles multiple
  46. * clients near-simultaneously.
  47. *
  48. * Interleaved log output can still be meaningful if each log entry
  49. * from different contexts had a distinctive stamp. This is where NDCs
  50. * come into play.
  51. *
  52. * <em><b>Note that NDCs are managed on a per thread
  53. * basis</b></em>. NDC operations such as {@link #push}, {@link
  54. * #pop}, {@link #clear}, {@link #getDepth} and {@link #setMaxDepth}
  55. * affect the NDC of the <em>current</em> thread only. NDCs of other
  56. * threads remain unaffected.
  57. *
  58. * For example, a server can build a per client request NDC
  59. * consisting the clients host name and other information contained in
  60. * the the request. <em>Cookies</em> are another source of distinctive
  61. * information. To build an NDC one uses the {@link #push}
  62. * operation. Simply put,
  63. *
  64. * <ul>
  65. * <li>Contexts can be nested.
  66. *
  67. * <li>When entering a context, call <code>getNDC().push()</code>. As a
  68. * side effect, if there is no nested diagnostic context for the
  69. * current thread, this method will create it.
  70. *
  71. * <li>When leaving a context, call <code>getNDC().pop()</code>.
  72. *
  73. * <li><b>When exiting a thread make sure to call {@link #remove
  74. * NDC.remove()}</b>.
  75. * </ul>
  76. *
  77. * There is no penalty for forgetting to match each
  78. * <code>push</code> operation with a corresponding <code>pop</code>,
  79. * except the obvious mismatch between the real application context
  80. * and the context set in the NDC. Use of the {@link NDCContextCreator}
  81. * class can automate this process and make your code exception-safe.
  82. *
  83. * If configured to do so, {@link log4cplus::PatternLayout} and
  84. * {@link log4cplus::TTCCLayout} instances automatically retrieve
  85. * the nested diagnostic context for the current thread without
  86. * any user intervention. Hence, even if a server is serving
  87. * multiple clients simultaneously, the logs emanating from the
  88. * same code (belonging to the same logger) can still be
  89. * distinguished because each client request will have a different
  90. * NDC tag.
  91. *
  92. * Heavy duty systems should call the {@link #remove} method when
  93. * leaving the run method of a thread. This ensures that the memory
  94. * used by the thread can be freed.
  95. *
  96. * A thread may inherit the nested diagnostic context of another
  97. * (possibly parent) thread using the {@link #inherit inherit}
  98. * method. A thread may obtain a copy of its NDC with the {@link
  99. * #cloneStack cloneStack} method and pass the reference to any other
  100. * thread, in particular to a child.
  101. */
  102. class LOG4CPLUS_EXPORT NDC
  103. {
  104. public:
  105. /**
  106. * Clear any nested diagnostic information if any. This method is
  107. * useful in cases where the same thread can be potentially used
  108. * over and over in different unrelated contexts.
  109. *
  110. * This method is equivalent to calling the {@link #setMaxDepth}
  111. * method with a zero <code>maxDepth</code> argument.
  112. */
  113. void clear();
  114. /**
  115. * Clone the diagnostic context for the current thread.
  116. *
  117. * Internally a diagnostic context is represented as a stack. A
  118. * given thread can supply the stack (i.e. diagnostic context) to a
  119. * child thread so that the child can inherit the parent thread's
  120. * diagnostic context.
  121. *
  122. * The child thread uses the {@link #inherit inherit} method to
  123. * inherit the parent's diagnostic context.
  124. *
  125. * @return Stack A clone of the current thread's diagnostic context.
  126. */
  127. DiagnosticContextStack cloneStack() const;
  128. /**
  129. * Inherit the diagnostic context of another thread.
  130. *
  131. * The parent thread can obtain a reference to its diagnostic
  132. * context using the {@link #cloneStack} method. It should
  133. * communicate this information to its child so that it may inherit
  134. * the parent's diagnostic context.
  135. *
  136. * The parent's diagnostic context is cloned before being
  137. * inherited. In other words, once inherited, the two diagnostic
  138. * contexts can be managed independently.
  139. *
  140. * @param stack The diagnostic context of the parent thread.
  141. */
  142. void inherit(const DiagnosticContextStack& stack);
  143. /**
  144. * Used when printing the diagnostic context.
  145. */
  146. log4cplus::tstring const & get() const;
  147. /**
  148. * Get the current nesting depth of this diagnostic context.
  149. *
  150. * @see #setMaxDepth
  151. */
  152. std::size_t getDepth() const;
  153. /**
  154. * Clients should call this method before leaving a diagnostic
  155. * context.
  156. *
  157. * The returned value is the value that was pushed last. If no
  158. * context is available, then the empty string "" is
  159. * returned. If each call to push() is paired with a call to
  160. * pop() (even in presence of thrown exceptions), the last
  161. * pop() call frees the memory used by NDC for this
  162. * thread. Otherwise, remove() must be called at the end of
  163. * the thread to free the memory used by NDC for the thread.
  164. *
  165. * @return String The innermost diagnostic context.
  166. *
  167. * @see NDCContextCreator, remove(), push()
  168. */
  169. log4cplus::tstring pop();
  170. /**
  171. * Same as pop() but without the return value.
  172. */
  173. void pop_void ();
  174. /**
  175. * Looks at the last diagnostic context at the top of this NDC
  176. * without removing it.
  177. *
  178. * The returned value is the value that was pushed last. If no
  179. * context is available, then the empty string "" is returned.
  180. *
  181. * @return String The innermost diagnostic context.
  182. */
  183. log4cplus::tstring const & peek() const;
  184. /**
  185. * Push new diagnostic context information for the current thread.
  186. *
  187. * The contents of the <code>message</code> parameter is
  188. * determined solely by the client. Each call to push() should
  189. * be paired with a call to pop().
  190. *
  191. * @param message The new diagnostic context information.
  192. *
  193. * @see NDCContextCreator, pop(), remove()
  194. */
  195. void push(const log4cplus::tstring& message);
  196. void push(tchar const * message);
  197. /**
  198. * Remove the diagnostic context for this thread.
  199. *
  200. * Each thread that created a diagnostic context by calling
  201. * push() should call this method before exiting. Otherwise,
  202. * the memory used by the thread cannot be reclaimed. It is
  203. * possible to omit this call if and only if each push() call
  204. * is always paired with a pop() call (even in presence of
  205. * thrown exceptions). Then the memory used by NDC will be
  206. * returned by the last pop() call and a call to remove() will
  207. * be no-op.
  208. */
  209. void remove();
  210. /**
  211. * Set maximum depth of this diagnostic context. If the current
  212. * depth is smaller or equal to <code>maxDepth</code>, then no
  213. * action is taken.
  214. *
  215. * This method is a convenient alternative to multiple {@link
  216. * #pop} calls. Moreover, it is often the case that at the end of
  217. * complex call sequences, the depth of the NDC is
  218. * unpredictable. The <code>setMaxDepth</code> method circumvents
  219. * this problem.
  220. *
  221. * For example, the combination
  222. * <pre>
  223. * void foo() {
  224. * &nbsp; std::size_t depth = NDC.getDepth();
  225. *
  226. * &nbsp; ... complex sequence of calls
  227. *
  228. * &nbsp; NDC.setMaxDepth(depth);
  229. * }
  230. * </pre>
  231. *
  232. * ensures that between the entry and exit of foo the depth of the
  233. * diagnostic stack is conserved.
  234. *
  235. * <b>Note:</b> Use of the {@link NDCContextCreator} class will solve
  236. * this particular problem.
  237. *
  238. * @see #getDepth
  239. */
  240. void setMaxDepth(std::size_t maxDepth);
  241. // Public ctor but only to be used by internal::DefaultContext.
  242. NDC();
  243. // Dtor
  244. virtual ~NDC();
  245. private:
  246. // Methods
  247. LOG4CPLUS_PRIVATE static DiagnosticContextStack* getPtr();
  248. template <typename StringType>
  249. LOG4CPLUS_PRIVATE
  250. void push_worker (StringType const &);
  251. // Disallow construction (and copying) except by getNDC()
  252. NDC(const NDC&);
  253. NDC& operator=(const NDC&);
  254. };
  255. /**
  256. * Return a reference to the singleton object.
  257. */
  258. LOG4CPLUS_EXPORT NDC& getNDC();
  259. /**
  260. * This is the internal object that is stored on the NDC stack.
  261. */
  262. struct LOG4CPLUS_EXPORT DiagnosticContext
  263. {
  264. // Ctors
  265. DiagnosticContext(const log4cplus::tstring& message,
  266. DiagnosticContext const * parent);
  267. DiagnosticContext(tchar const * message,
  268. DiagnosticContext const * parent);
  269. DiagnosticContext(const log4cplus::tstring& message);
  270. DiagnosticContext(tchar const * message);
  271. DiagnosticContext(DiagnosticContext const &);
  272. DiagnosticContext & operator = (DiagnosticContext const &);
  273. #if defined (LOG4CPLUS_HAVE_RVALUE_REFS)
  274. DiagnosticContext(DiagnosticContext &&);
  275. DiagnosticContext & operator = (DiagnosticContext &&);
  276. #endif
  277. void swap (DiagnosticContext &);
  278. // Data
  279. log4cplus::tstring message; /*!< The message at this context level. */
  280. log4cplus::tstring fullMessage; /*!< The entire message stack. */
  281. };
  282. /**
  283. * This class ensures that a {@link NDC#push} call is always matched with
  284. * a {@link NDC#pop} call even in the face of exceptions.
  285. */
  286. class LOG4CPLUS_EXPORT NDCContextCreator {
  287. public:
  288. /** Pushes <code>msg</code> onto the NDC stack. */
  289. NDCContextCreator(const log4cplus::tstring& msg);
  290. NDCContextCreator(tchar const * msg);
  291. /** Pops the NDC stack. */
  292. ~NDCContextCreator();
  293. };
  294. } // end namespace log4cplus
  295. #endif // _LO4CPLUS_NDC_HEADER_