--- /dev/null
+/*
+ * 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.
+ */
+
+// Contributors: Dan Milstein
+// Ray Millard
+
+package org.apache.log4j;
+
+import java.util.Hashtable;
+import java.util.Stack;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.log4j.helpers.LogLog;
+
+/**
+ 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
+ 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 {@link #push push}, {@link
+ #pop}, {@link #clear}, {@link #getDepth} and {@link #setMaxDepth}
+ 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 {@link #push 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 {@link #remove
+ NDC.remove()}</b>.
+ </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, {@link PatternLayout} and {@link
+ TTCCLayout} 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 category) can still be distinguished because each client
+ request will have a different NDC tag.
+
+ <p>Heavy duty systems should call the {@link #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 {@link #remove} before exiting a
+ thread.
+
+ <p>A thread may inherit the nested diagnostic context of another
+ (possibly parent) thread using the {@link #inherit inherit}
+ method. A thread may obtain a copy of its NDC with the {@link
+ #cloneStack cloneStack} method and pass the reference to any other
+ thread, in particular to a child.
+
+ @author Ceki Gülcü
+ @since 0.7.0
+
+*/
+
+public class NDC {
+
+ // The synchronized keyword is not used in this class. This may seem
+ // dangerous, especially since the class will be used by
+ // multiple-threads. In particular, all threads share the same
+ // hashtable (the "ht" variable). This is OK since java hashtables
+ // are thread safe. Same goes for Stacks.
+
+ // More importantly, when inheriting diagnostic contexts the child
+ // thread is handed a clone of the parent's NDC. It follows that
+ // each thread has its own NDC (i.e. stack).
+
+ static Hashtable ht = new Hashtable();
+
+ static int pushCounter = 0; // the number of times push has been called
+ // after the latest call to lazyRemove
+
+ // The number of times we allow push to be called before we call lazyRemove
+ // 5 is a relatively small number. As such, lazyRemove is not called too
+ // frequently. We thus avoid the cost of creating an Enumeration too often.
+ // The higher this number, the longer is the avarage period for which all
+ // logging calls in all threads are blocked.
+ static final int REAP_THRESHOLD = 5;
+
+ // No instances allowed.
+ private NDC() {}
+
+ /**
+ * Get NDC stack for current thread.
+ * @return NDC stack for current thread.
+ */
+ private static Stack getCurrentStack() {
+ if (ht != null) {
+ return (Stack) ht.get(Thread.currentThread());
+ }
+ return null;
+ }
+
+
+ /**
+ Clear any nested diagnostic information if any. This method is
+ useful in cases where the same thread can be potentially used
+ over and over in different unrelated contexts.
+
+ <p>This method is equivalent to calling the {@link #setMaxDepth}
+ method with a zero <code>maxDepth</code> argument.
+
+ @since 0.8.4c */
+ public
+ static
+ void clear() {
+ Stack stack = getCurrentStack();
+ if(stack != null) {
+ stack.setSize(0);
+ }
+ }
+
+
+ /**
+ Clone the diagnostic context for the current thread.
+
+ <p>Internally a diagnostic context is represented as a stack. A
+ given thread can supply the stack (i.e. diagnostic context) to a
+ child thread so that the child can inherit the parent thread's
+ diagnostic context.
+
+ <p>The child thread uses the {@link #inherit inherit} method to
+ inherit the parent's diagnostic context.
+
+ @return Stack A clone of the current thread's diagnostic context.
+
+ */
+ public
+ static
+ Stack cloneStack() {
+ Stack stack = getCurrentStack();
+ if(stack == null) {
+ return null;
+ } else {
+ return (Stack) stack.clone();
+ }
+ }
+
+
+ /**
+ Inherit the diagnostic context of another thread.
+
+ <p>The parent thread can obtain a reference to its diagnostic
+ context using the {@link #cloneStack} method. It should
+ communicate this information to its child so that it may inherit
+ the parent's diagnostic context.
+
+ <p>The parent's diagnostic context is cloned before being
+ inherited. In other words, once inherited, the two diagnostic
+ contexts can be managed independently.
+
+ <p>In java, a child thread cannot obtain a reference to its
+ parent, unless it is directly handed the reference. Consequently,
+ there is no client-transparent way of inheriting diagnostic
+ contexts. Do you know any solution to this problem?
+
+ @param stack The diagnostic context of the parent thread.
+
+ */
+ public
+ static
+ void inherit(Stack stack) {
+ if(stack != null) {
+ ht.put(Thread.currentThread(), stack);
+ }
+ }
+
+
+ /**
+ <font color="#FF4040"><b>Never use this method directly, use the {@link
+ org.apache.log4j.spi.LoggingEvent#getNDC} method instead</b></font>.
+ */
+ static
+ public
+ String get() {
+ Stack s = getCurrentStack();
+ if(s != null && !s.isEmpty()) {
+ return ((DiagnosticContext) s.peek()).fullMessage;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the current nesting depth of this diagnostic context.
+ *
+ * @see #setMaxDepth
+ * @since 0.7.5
+ */
+ public
+ static
+ int getDepth() {
+ Stack stack = getCurrentStack();
+ if(stack == null) {
+ return 0;
+ } else {
+ return stack.size();
+ }
+ }
+
+ private
+ static
+ void lazyRemove() {
+ if (ht == null) {
+ return;
+ }
+
+ // The synchronization on ht is necessary to prevent JDK 1.2.x from
+ // throwing ConcurrentModificationExceptions at us. This sucks BIG-TIME.
+ // One solution is to write our own hashtable implementation.
+ Vector v;
+
+ synchronized(ht) {
+ // Avoid calling clean-up too often.
+ if(++pushCounter <= REAP_THRESHOLD) {
+ return; // We release the lock ASAP.
+ } else {
+ pushCounter = 0; // OK let's do some work.
+ }
+
+ int misses = 0;
+ v = new Vector();
+ Enumeration enumeration = ht.keys();
+ // We give up after 4 straigt missses. That is 4 consecutive
+ // inspected threads in 'ht' that turn out to be alive.
+ // The higher the proportion on dead threads in ht, the higher the
+ // chances of removal.
+ while(enumeration.hasMoreElements() && (misses <= 4)) {
+ Thread t = (Thread) enumeration.nextElement();
+ if(t.isAlive()) {
+ misses++;
+ } else {
+ misses = 0;
+ v.addElement(t);
+ }
+ }
+ } // synchronized
+
+ int size = v.size();
+ for(int i = 0; i < size; i++) {
+ Thread t = (Thread) v.elementAt(i);
+ LogLog.debug("Lazy NDC removal for thread [" + t.getName() + "] ("+
+ ht.size() + ").");
+ ht.remove(t);
+ }
+ }
+
+ /**
+ Clients should call this method before leaving a diagnostic
+ context.
+
+ <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.
+
+ */
+ public
+ static
+ String pop() {
+ Stack stack = getCurrentStack();
+ if(stack != null && !stack.isEmpty()) {
+ return ((DiagnosticContext) stack.pop()).message;
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ Looks at the last diagnostic context at the top of this NDC
+ 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.
+
+ */
+ public
+ static
+ String peek() {
+ Stack stack = getCurrentStack();
+ if(stack != null && !stack.isEmpty()) {
+ return ((DiagnosticContext) stack.peek()).message;
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ 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. */
+ public
+ static
+ void push(String message) {
+ Stack stack = getCurrentStack();
+
+ if(stack == null) {
+ DiagnosticContext dc = new DiagnosticContext(message, null);
+ stack = new Stack();
+ Thread key = Thread.currentThread();
+ ht.put(key, stack);
+ stack.push(dc);
+ } else if (stack.isEmpty()) {
+ DiagnosticContext dc = new DiagnosticContext(message, null);
+ stack.push(dc);
+ } else {
+ DiagnosticContext parent = (DiagnosticContext) stack.peek();
+ stack.push(new DiagnosticContext(message, parent));
+ }
+ }
+
+ /**
+ Remove the diagnostic context for this thread.
+
+ <p>Each thread that created a diagnostic context by calling
+ {@link #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 {@link #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.
+
+ */
+ static
+ public
+ void remove() {
+ if (ht != null) {
+ ht.remove(Thread.currentThread());
+
+ // Lazily remove dead-thread references in ht.
+ lazyRemove();
+ }
+ }
+
+ /**
+ Set maximum depth of this diagnostic context. If the current
+ depth is smaller or equal to <code>maxDepth</code>, then no
+ action is taken.
+
+ <p>This method is a convenient alternative to multiple {@link
+ #pop} calls. Moreover, it is often the case that at the end of
+ complex call sequences, the depth of the NDC is
+ unpredictable. The <code>setMaxDepth</code> method circumvents
+ this problem.
+
+ <p>For example, the combination
+ <pre>
+ void foo() {
+ int depth = NDC.getDepth();
+
+ ... complex sequence of calls
+
+ NDC.setMaxDepth(depth);
+ }
+ </pre>
+
+ ensures that between the entry and exit of foo the depth of the
+ diagnostic stack is conserved.
+
+ @see #getDepth
+ @since 0.7.5 */
+ static
+ public
+ void setMaxDepth(int maxDepth) {
+ Stack stack = getCurrentStack();
+ if(stack != null && maxDepth < stack.size()) {
+ stack.setSize(maxDepth);
+ }
+ }
+
+ // =====================================================================
+ private static class DiagnosticContext {
+
+ String fullMessage;
+ String message;
+
+ DiagnosticContext(String message, DiagnosticContext parent) {
+ this.message = message;
+ if(parent != null) {
+ fullMessage = parent.fullMessage + ' ' + message;
+ } else {
+ fullMessage = message;
+ }
+ }
+ }
+}
+