You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by sw...@apache.org on 2023/06/28 02:15:54 UTC
[logging-log4cxx] branch master updated: Improve the NDC documentation and remove Java specific statements (#221)
This is an automated email from the ASF dual-hosted git repository.
swebb2066 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git
The following commit(s) were added to refs/heads/master by this push:
new 7d1a456b Improve the NDC documentation and remove Java specific statements (#221)
7d1a456b is described below
commit 7d1a456b5daae3c113b1ac0eaaf2b2abbdf7d92a
Author: Stephen Webb <st...@ieee.org>
AuthorDate: Wed Jun 28 12:15:49 2023 +1000
Improve the NDC documentation and remove Java specific statements (#221)
---
src/main/include/log4cxx/mdc.h | 88 ++++-----
src/main/include/log4cxx/ndc.h | 245 ++++++++++--------------
src/site/markdown/1-usage.md | 1 +
src/site/markdown/nested-diagnostic-contexts.md | 69 +++++++
src/site/markdown/usage.md | 49 -----
5 files changed, 214 insertions(+), 238 deletions(-)
diff --git a/src/main/include/log4cxx/mdc.h b/src/main/include/log4cxx/mdc.h
index 01d44d55..a73c609a 100644
--- a/src/main/include/log4cxx/mdc.h
+++ b/src/main/include/log4cxx/mdc.h
@@ -26,12 +26,21 @@ namespace log4cxx
{
/**
-The MDC class is similar to the {@link log4cxx::NDC NDC} class except that it is
-based on a map instead of a stack. It provides <em>mapped
-diagnostic contexts</em>. A <em>Mapped Diagnostic Context</em>, or
+A <em>Mapped Diagnostic Context</em>, or
MDC in short, is an instrument for distinguishing interleaved log
output from different sources. Log output is typically interleaved
when a server handles multiple clients near-simultaneously.
+
+#MDC provides a constructor and destructor which simply call the #put and
+#remove methods, allowing for automatic cleanup when the current scope ends.
+
+#MDC operations such as #put, #remove and #clear
+affect only logging events emitted in the <em>calling</em> thread.
+The contexts of other threads are not changed.
+That is, <em><b>contexts are managed on a per thread basis</b></em>.
+
+The MDC class is similar to the {@link log4cxx::NDC NDC} class except that it is
+based on a map instead of a stack.
*/
class LOG4CXX_EXPORT MDC
{
@@ -44,27 +53,23 @@ class LOG4CXX_EXPORT MDC
* Places a key/value pair in the MDC for the current thread
* which will be removed during the corresponding destructor. Both
* construction and destruction are expected to be on the same thread.
- * @param key key
- * @param value value.
+ * @param key context identifier
+ * @param value a string that distinguishes this context.
*/
MDC(const std::string& key, const std::string& value);
~MDC();
/**
- * Put a context value (the <code>o</code> parameter) as identified
- * with the <code>key</code> parameter into the current thread's
- * context map.
+ * Set the <code>key</code> context in the current thread's context map to <code>value</code>.
*
* <p>If the current thread does not have a context map it is
* created as a side effect.
- * @param key key
- * @param value value.
+ * @param key context identifier
+ * @param value a string that distinguishes this context.
*/
static void put(const std::string& key, const std::string& value);
/**
- * Put a context value (the <code>o</code> parameter) as identified
- * with the <code>key</code> parameter into the current thread's
- * context map.
+ * Set the <code>key</code> context in the current thread's context map to <code>value</code>.
*
* <p>If the current thread does not have a context map it is
* created as a side effect.
@@ -75,7 +80,7 @@ class LOG4CXX_EXPORT MDC
* Get the context identified by the <code>key</code> parameter.
*
* <p>This method has no side effects.
- * @param key key.
+ * @param key context identifier.
* @return value for key, empty if not set.
* */
static std::string get(const std::string& key);
@@ -90,7 +95,7 @@ class LOG4CXX_EXPORT MDC
/**
* Remove the the context identified by the <code>key</code>
* parameter.
- * @param key key.
+ * @param key context identifier.
* @return value if key had been set, empty if not.
*/
static std::string remove(const std::string& key);
@@ -99,33 +104,31 @@ class LOG4CXX_EXPORT MDC
* Places a key/value pair in the MDC for the current thread
* which will be removed during the corresponding destructor. Both
* construction and destruction are expected to be on the same thread.
- * @param key key
- * @param value value.
+ * @param key context identifier
+ * @param value a string that distinguishes this context.
*/
MDC(const std::wstring& key, const std::wstring& value);
/**
- * Put a context value (the <code>o</code> parameter) as identified
- * with the <code>key</code> parameter into the current thread's
- * context map.
+ * Set the <code>key</code> context in the current thread's context map to <code>value</code>.
*
* <p>If the current thread does not have a context map it is
* created as a side effect.
- * @param key key
- * @param value value.
+ * @param key context identifier
+ * @param value a string that distinguishes this context.
*/
static void put(const std::wstring& key, const std::wstring& value);
/**
* Get the context identified by the <code>key</code> parameter.
*
* <p>This method has no side effects.
- * @param key key.
+ * @param key context identifier.
* @return value for key, empty if not set.
* */
static std::wstring get(const std::wstring& key);
/**
* Remove the the context identified by the <code>key</code>
* parameter.
- * @param key key.
+ * @param key context identifier.
* @return value if key had been set, empty if not.
*/
static std::wstring remove(const std::wstring& key);
@@ -135,33 +138,31 @@ class LOG4CXX_EXPORT MDC
* Places a key/value pair in the MDC for the current thread
* which will be removed during the corresponding destructor. Both
* construction and destruction are expected to be on the same thread.
- * @param key key
- * @param value value.
+ * @param key context identifier
+ * @param value a string that distinguishes this context.
*/
MDC(const std::basic_string<UniChar>& key, const std::basic_string<UniChar>& value);
/**
- * Put a context value (the <code>o</code> parameter) as identified
- * with the <code>key</code> parameter into the current thread's
- * context map.
+ * Set the <code>key</code> context in the current thread's context map to <code>value</code>.
*
* <p>If the current thread does not have a context map it is
* created as a side effect.
- * @param key key
- * @param value value.
+ * @param key context identifier
+ * @param value the value.
*/
static void put(const std::basic_string<UniChar>& key, const std::basic_string<UniChar>& value);
/**
* Get the context identified by the <code>key</code> parameter.
*
* <p>This method has no side effects.
- * @param key key.
+ * @param key context identifier.
* @return value for key, empty if not set.
* */
static std::basic_string<UniChar> get(const std::basic_string<UniChar>& key);
/**
* Remove the the context identified by the <code>key</code>
* parameter.
- * @param key key.
+ * @param key context identifier.
* @return value if key had been set, empty if not.
*/
static std::basic_string<UniChar> remove(const std::basic_string<UniChar>& key);
@@ -171,33 +172,31 @@ class LOG4CXX_EXPORT MDC
* Places a key/value pair in the MDC for the current thread
* which will be removed during the corresponding destructor. Both
* construction and destruction are expected to be on the same thread.
- * @param key key
- * @param value value.
+ * @param key context identifier
+ * @param value a string that distinguishes this context.
*/
MDC(const CFStringRef& key, const CFStringRef& value);
/**
- * Put a context value (the <code>o</code> parameter) as identified
- * with the <code>key</code> parameter into the current thread's
- * context map.
+ * Set the <code>key</code> context in the current thread's context map to <code>value</code>.
*
* <p>If the current thread does not have a context map it is
* created as a side effect.
- * @param key key
- * @param value value.
+ * @param key context identifier
+ * @param value a string that distinguishes this context.
*/
static void put(const CFStringRef& key, const CFStringRef& value);
/**
* Get the context identified by the <code>key</code> parameter.
*
* <p>This method has no side effects.
- * @param key key.
+ * @param key context identifier.
* @return value for key, empty if not set.
* */
static CFStringRef get(const CFStringRef& key);
/**
* Remove the the context identified by the <code>key</code>
* parameter.
- * @param key key.
+ * @param key context identifier.
* @return value if key had been set, empty if not.
*/
static CFStringRef remove(const CFStringRef& key);
@@ -205,7 +204,7 @@ class LOG4CXX_EXPORT MDC
/**
* Remove the the context identified by the <code>key</code>
* parameter.
- * @param key key.
+ * @param key context identifier.
* @param prevValue buffer to which previous value is appended.
* @return true if key existed in MDC.
*/
@@ -213,6 +212,9 @@ class LOG4CXX_EXPORT MDC
/**
* Clear all entries in the MDC.
+ * <p>A thread that adds to the diagnostic context by calling
+ * #put should call this method before exiting
+ * to prevent unbounded memory usage.
*/
static void clear();
diff --git a/src/main/include/log4cxx/ndc.h b/src/main/include/log4cxx/ndc.h
index c2642cd8..549949c6 100644
--- a/src/main/include/log4cxx/ndc.h
+++ b/src/main/include/log4cxx/ndc.h
@@ -26,65 +26,53 @@ namespace log4cxx
{
/**
-the ndc class implements <i>nested diagnostic contexts</i> as
-defined by neil harrison in the article "patterns for logging
-diagnostic messages" part of the book "<i>pattern languages of
-program design 3</i>" edited by martin et al.
-
-<p>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
+A <em>Nested Diagnostic Context</em>, 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
clients near-simultaneously.
-
-<p>interleaved log output can still be meaningful if each log entry
-from different contexts had a distinctive stamp. this is where ndcs
-come into play.
-
-<p><em><b>note that ndcs are managed on a per thread
-basis</b></em>. ndc operations such as #push,
-#pop, #clear and #getDepth
-affect the ndc of the <em>current</em> thread only. ndcs of other
-threads remain unaffected.
-
-<p>for example, a servlet can build a per client request ndc
-consisting the clients host name and other information contained in
-the the request. <em>cookies</em> are another source of distinctive
-information. to build an ndc one uses the #push
-operation. simply put,
-
-<p><ul>
- <li>contexts can be nested.
-
- <p><li>when entering a context, call <code>ndc.push</code>. as a
- side effect, if there is no nested diagnostic context for the
- current thread, this method will create it.
-
- <p><li>when leaving a context, call <code>ndc.pop</code>.
-
- <p><li><b>when exiting a thread make sure to call #remove
- </b>.
+Interleaved log output can still be meaningful if each log entry
+from different contexts have a distinctive stamp.
+This is where contexts come into play.
+
+#NDC provides a constructor and destructor which simply call the #push and
+#pop methods, allowing for automatic cleanup when the current scope ends.
+
+#NDC operations such as #push, #pop, #clear and #remove
+affect only logging events emitted in the <em>calling</em> thread.
+The contexts of other threads are not changed.
+That is, <em><b>contexts are managed on a per thread basis</b></em>.
+
+For example, a servlet can build a per client request context
+consisting of the client's host name and other information contained in
+the the request. <em>Cookies</em> are another source of distinctive
+information.
+
+Contexts can be nested:
+<ul>
+ <li>when entering a context, initialize a <code>log4cxx::NDC</code>
+ type variable with a distinctive string.
+ If there is no nested diagnostic context for the
+ current thread, a NDC stack will be created.
+ The distinctive string will be automatically removed from the
+ current thread's context stack when the variable goes out of scope.
+
+ <li>when exiting a thread call NDC::remove to deal with any
+ #push operation not matched with a corresponding #pop.
</ul>
-<p>there is no penalty for forgetting to match each
-<code>push</code> operation with a corresponding <code>pop</code>,
-except the obvious mismatch between the real application context
-and the context set in the ndc.
-
-<p>if configured to do so, PatternLayout and
-TTCCLayout instances automatically retrieve the nested diagnostic
+If configured to do so, PatternLayout, xml::XMLLayout and
+JSONLayout instances automatically retrieve the nested diagnostic
context for the current thread without any user intervention.
-hence, even if a servlet is serving multiple clients
-simultaneously, the logs emanating from the same code (belonging to
-the same logger) can still be distinguished because each client
-request will have a different ndc tag.
-
-<p>heavy duty systems should call the #remove method when
-leaving the run method of a thread. this ensures that the memory
-used by the thread can be freed by the java garbage
-collector. there is a mechanism to lazily remove references to dead
-threads. in practice, this means that you can be a little sloppy
-and sometimes forget to call #remove before exiting a
-thread.
+hence, even if a process is serving multiple clients simultaneously,
+the logging events emanating from the same code
+(belonging to the same logger)
+can still be distinguished because each client
+request will have a different tag.
+
+#NDC implements <i>nested diagnostic contexts</i> as
+defined by Neil Harrison in the article "Patterns for Logging
+Diagnostic Messages" part of the book <i>"Pattern Languages of
+Program Design 3"</i> edited by Martin et al.
*/
class LOG4CXX_EXPORT NDC
@@ -97,21 +85,15 @@ class LOG4CXX_EXPORT NDC
typedef std::stack<DiagnosticContext> Stack;
/**
- Creates a nested diagnostic context.
- Since java performs no automatic cleanup of objects when a
- scope is left, in log4j push() and pop() must be used
- to manage the NDC. For convenience, log4cxx provides
- an NDC constructor and destructor which simply call the push() and
- pop() methods, allowing for automatic cleanup when the current
- scope ends.
-
- @param message The new diagnostic context information.
+ Add \c message onto the context stack.
@see The #push method.
+
+ @param message The text added to the diagnostic context information.
*/
NDC(const std::string& message);
/**
- Removes the topmost element from the NDC stack.
+ Remove the topmost element from the context stack associated with the current thread.
@see The #pop method.
*/
@@ -171,90 +153,75 @@ class LOG4CXX_EXPORT NDC
static bool empty();
/**
- Pop top value off stack.
- @return top value.
+ Get the value at the top of the stack
+ associated with the current thread and then remove it.
+ <p>The returned value is the value that was pushed last. If no
+ context is available, then the empty string "" is returned.
+ @return String The text of the innermost diagnostic context.
*/
static LogString pop();
/**
- Pop top value off stack.
+ Append to \c buf the top value in the stack associated with the current thread and then remove it.
@param buf to which top value is appended.
@return true if NDC contained at least one value.
*/
static bool pop(std::string& buf);
/**
- Looks at the last diagnostic context at the top of this NDC
- without removing it.
+ Get the value at the top of the stack
+ associated with the current thread without removing it.
<p>The returned value is the value that was pushed last. If no
context is available, then the empty string "" is returned.
- @return String The innermost diagnostic context.
+ @return String The text of the innermost diagnostic context.
*/
static LogString peek();
/**
- Get top value without removing value.
+ Append to \c buf the top value in the stack associated with the current thread without removing it.
@param buf to which top value is appended.
@return true if NDC contained at least one value.
*/
static bool peek(std::string& buf);
/**
- Push new diagnostic context information for the current thread.
+ Add \c message to the stack associated with the current thread.
<p>The contents of the <code>message</code> parameter is
determined solely by the client.
- @param message The new diagnostic context information.
+ @param message The text added to the diagnostic context information.
*/
static void push(const std::string& message);
/**
- Push new diagnostic context information for the current thread.
+ Add \c message to the stack associated with the current thread.
<p>The contents of the <code>message</code> parameter is
determined solely by the client.
- @param message The new diagnostic context information.
+ @param message The text added to the diagnostic context information.
*/
static void pushLS(const LogString& message);
/**
- Remove the diagnostic context for this thread.
- <p>Each thread that created a diagnostic context by calling
- #push should call this method before exiting. Otherwise,
- the memory used by the <b>thread</b> cannot be reclaimed by the
- VM.
- <p>As this is such an important problem in heavy duty systems and
- because it is difficult to always guarantee that the remove
- method is called before exiting a thread, this method has been
- augmented to lazily remove references to dead threads. In
- practice, this means that you can be a little sloppy and
- occasionally forget to call #remove before exiting a
- thread. However, you must call <code>remove</code> sometime. If
- you never call it, then your application is sure to run out of
- memory.
+ Remove all the diagnostic context data for this thread.
+ <p>A thread that adds to a diagnostic context by calling
+ #push should call this method before exiting
+ to prevent unbounded memory usage.
*/
static void remove();
#if LOG4CXX_WCHAR_T_API
/**
- Creates a nested diagnostic context.
- Since java performs no automatic cleanup of objects when a
- scope is left, in log4j push() and pop() must be used
- to manage the NDC. For convenience, log4cxx provides
- an NDC constructor and destructor which simply call the push() and
- pop() methods, allowing for automatic cleanup when the current
- scope ends.
-
- @param message The new diagnostic context information.
- @see The #push method.
+ Add \c message onto the context stack.
+ @see The #push method.
+
+ @param message The text added to the diagnostic context information.
*/
NDC(const std::wstring& message);
/**
- Push new diagnostic context information for the current thread.
- <p>The contents of the <code>message</code> parameter is
- determined solely by the client.
- @param message The new diagnostic context information.
+ Add \c message to the stack associated with the current thread.
+ @param message The text added to the diagnostic context information.
*/
static void push(const std::wstring& message);
/**
- * Appends the current NDC content to the provided string.
- * @param dst destination.
- * @return true if NDC value set.
+ Append to \c dst the top value in the stack associated with the current thread without removing it.
+ @param dst to which top value is appended.
+ @return true if NDC contained at least one value.
*/
static bool peek(std::wstring& dst);
/**
@@ -266,69 +233,55 @@ class LOG4CXX_EXPORT NDC
#endif
#if LOG4CXX_UNICHAR_API
/**
- Creates a nested diagnostic context.
- Since java performs no automatic cleanup of objects when a
- scope is left, in log4j push() and pop() must be used
- to manage the NDC. For convenience, log4cxx provides
- an NDC constructor and destructor which simply call the push() and
- pop() methods, allowing for automatic cleanup when the current
- scope ends.
-
- @param message The new diagnostic context information.
- @see The #push method.
- */
+ Add \c message onto the context stack.
+ @see The #push method.
+
+ @param message The text added to the diagnostic context information.
+ */
NDC(const std::basic_string<UniChar>& message);
/**
- Push new diagnostic context information for the current thread.
+ Add \c message to the stack associated with the current thread.
<p>The contents of the <code>message</code> parameter is
determined solely by the client.
- @param message The new diagnostic context information.
+ @param message The text added to the diagnostic context information.
*/
static void push(const std::basic_string<UniChar>& message);
/**
- * Appends the current NDC content to the provided string.
- * @param dst destination.
- * @return true if NDC value set.
+ Append to \c dst the top value in the stack associated with the current thread without removing it.
+ @param dst to which top value is appended.
+ @return true if NDC contained at least one value.
*/
static bool peek(std::basic_string<UniChar>& dst);
/**
- * Appends the current NDC content to the provided string and removes the value from the NDC.
- * @param dst destination.
- * @return true if NDC value set.
+ Append to \c dst the top value in the stack associated with the current thread and then remove it.
+ @param dst to which top value is appended.
+ @return true if NDC contained at least one value.
*/
static bool pop(std::basic_string<UniChar>& dst);
#endif
#if LOG4CXX_CFSTRING_API
/**
- Creates a nested diagnostic context.
- Since java performs no automatic cleanup of objects when a
- scope is left, in log4j push() and pop() must be used
- to manage the NDC. For convenience, log4cxx provides
- an NDC constructor and destructor which simply call the push() and
- pop() methods, allowing for automatic cleanup when the current
- scope ends.
-
- @param message The new diagnostic context information.
- @see The #push method.
+ Add \c message onto the context stack.
+ @see The #push method.
+
+ @param message The text added to the diagnostic context information.
*/
NDC(const CFStringRef& message);
/**
- Push new diagnostic context information for the current thread.
- <p>The contents of the <code>message</code> parameter is
- determined solely by the client.
- @param message The new diagnostic context information.
+ Add \c message to the stack associated with the current thread.
+ @param message The text added to the diagnostic context information.
*/
static void push(const CFStringRef& message);
/**
- * Gets the current NDC value.
- * @param dst destination.
- * @return true if NDC value set.
+ Append to \c dst the top value in the stack associated with the current thread without removing it.
+ @param dst to which top value is appended.
+ @return true if NDC contained at least one value.
*/
static bool peek(CFStringRef& dst);
/**
- * Gets and removes the current NDC value.
- * @param dst destination.
- * @return true if NDC value set.
+ Append to \c dst the top value in the stack associated with the current thread and then remove it.
+ @param dst to which top value is appended.
+ @return true if NDC contained at least one value.
*/
static bool pop(CFStringRef& dst);
#endif
diff --git a/src/site/markdown/1-usage.md b/src/site/markdown/1-usage.md
index 5ce8aecf..4cb5694e 100644
--- a/src/site/markdown/1-usage.md
+++ b/src/site/markdown/1-usage.md
@@ -24,6 +24,7 @@ Usage {#usage-overview}
See the following pages for usage information:
* @subpage usage
+* @subpage nested-diagnostic-contexts
* @subpage filters
* @subpage threading
* @subpage extending-log4cxx
diff --git a/src/site/markdown/nested-diagnostic-contexts.md b/src/site/markdown/nested-diagnostic-contexts.md
new file mode 100644
index 00000000..994d53a8
--- /dev/null
+++ b/src/site/markdown/nested-diagnostic-contexts.md
@@ -0,0 +1,69 @@
+Nested Diagnostic Contexts {#nested-diagnostic-contexts}
+===
+<!--
+ Note: License header cannot be first, as doxygen does not generate
+ cleanly if it before the '==='
+-->
+<!--
+ 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.
+-->
+
+Most real-world systems have to deal with multiple clients
+simultaneously. In a typical multithreaded implementation of such a
+system, different threads will handle different clients. Logging is
+especially well suited to trace and debug complex distributed
+applications. A common approach to differentiate the logging output of
+one client from another is to instantiate a new separate logger for each
+client. This promotes the proliferation of loggers and increases the
+management overhead of logging.
+
+A lighter technique is to uniquely stamp each log request initiated from
+the same client interaction. Neil Harrison described this method in the
+book "Patterns for Logging Diagnostic Messages," in *Pattern Languages
+of Program Design 3*, edited by R. Martin, D. Riehle, and F. Buschmann
+(Addison-Wesley, 1997).
+
+To uniquely stamp each request, the user pushes contextual information
+into the *Nested Diagnostic Context* (NDC) using the *log4cxx::NDC* class.
+For an example refer to \ref trivial.cpp.
+Note that all methods of the *log4cxx::NDC* class are static.
+
+The NDC is managed per thread as a *stack* of contextual information.
+Each log entry will include the entire stack
+for the current thread (for better control use *log4cxx::MDC*).
+The user is responsible for placing the correct information in the NDC
+by using the *push* and *pop* methods at
+a few well-defined points in the code. In contrast, the per-client
+logger approach commands extensive changes in the code.
+
+To illustrate this point, let us take the example of a servlet
+delivering content to numerous clients. The servlet can build the NDC at
+the very beginning of the request before executing other code. The
+contextual information can be the client's host name and other
+information inherent to the request, typically information contained in
+cookies. Hence, even if the servlet is serving multiple clients
+simultaneously, the logs initiated by the same code, i.e. belonging to
+the same logger, can still be distinguished because each client request
+will have a different NDC stack. Contrast this with the complexity of
+passing a freshly instantiated logger to all code exercised during the
+client's request.
+
+Nevertheless, some sophisticated applications, such as virtual hosting
+web servers, must log differently depending on the virtual host context
+and also depending on the software component issuing the request. Recent
+Log4cxx releases support multiple hierarchy trees. This enhancement
+allows each virtual host to possess its own copy of the logger
+hierarchy.
diff --git a/src/site/markdown/usage.md b/src/site/markdown/usage.md
index 14010d19..2450fdbb 100644
--- a/src/site/markdown/usage.md
+++ b/src/site/markdown/usage.md
@@ -549,55 +549,6 @@ To use automatic configuration with a non-standard file name
create and use your own wrapper for [getLogger](@ref log4cxx.LogManager.getLogger).
A full example can be seen in the \ref com/foo/config3.cpp file.
-# Nested Diagnostic Contexts {#nested-diagnostic-contexts}
-
-Most real-world systems have to deal with multiple clients
-simultaneously. In a typical multithreaded implementation of such a
-system, different threads will handle different clients. Logging is
-especially well suited to trace and debug complex distributed
-applications. A common approach to differentiate the logging output of
-one client from another is to instantiate a new separate logger for each
-client. This promotes the proliferation of loggers and increases the
-management overhead of logging.
-
-A lighter technique is to uniquely stamp each log request initiated from
-the same client interaction. Neil Harrison described this method in the
-book "Patterns for Logging Diagnostic Messages," in *Pattern Languages
-of Program Design 3*, edited by R. Martin, D. Riehle, and F. Buschmann
-(Addison-Wesley, 1997).
-
-To uniquely stamp each request, the user pushes contextual information
-into the *Nested Diagnostic Context* (NDC) using the *log4cxx::NDC* class.
-For an example refer to \ref trivial.cpp.
-Note that all methods of the *log4cxx::NDC* class are static.
-
-The NDC is managed per thread as a *stack* of contextual information.
-Each log entry will include the entire stack
-for the current thread (for better control use *log4cxx::MDC*).
-The user is responsible for placing the correct information in the NDC
-by using the *push* and *pop* methods at
-a few well-defined points in the code. In contrast, the per-client
-logger approach commands extensive changes in the code.
-
-To illustrate this point, let us take the example of a servlet
-delivering content to numerous clients. The servlet can build the NDC at
-the very beginning of the request before executing other code. The
-contextual information can be the client's host name and other
-information inherent to the request, typically information contained in
-cookies. Hence, even if the servlet is serving multiple clients
-simultaneously, the logs initiated by the same code, i.e. belonging to
-the same logger, can still be distinguished because each client request
-will have a different NDC stack. Contrast this with the complexity of
-passing a freshly instantiated logger to all code exercised during the
-client's request.
-
-Nevertheless, some sophisticated applications, such as virtual hosting
-web servers, must log differently depending on the virtual host context
-and also depending on the software component issuing the request. Recent
-Log4cxx releases support multiple hierarchy trees. This enhancement
-allows each virtual host to possess its own copy of the logger
-hierarchy.
-
# Logging Custom Types {#custom-types}
Often, the data that needs to be logged is not just standard data types