All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.log4j.NDC Maven / Gradle / Ivy

There is a newer version: 1.2.25
Show newest version
/*
 * 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 nested diagnostic contexts as defined by Neil
 * Harrison in the article "Patterns for Logging Diagnostic Messages" part of
 * the book "Pattern Languages of Program Design 3" edited by Martin et
 * al.
 * 
 * 

* 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. * *

* 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. * *

* Note that NDCs are managed on a per thread basis. NDC * operations such as {@link #push push}, {@link #pop}, {@link #clear}, * {@link #getDepth} and {@link #setMaxDepth} affect the NDC of the * current thread only. NDCs of other threads remain unaffected. * *

* For example, a servlet can build a per client request NDC consisting the * clients host name and other information contained in the the request. * Cookies are another source of distinctive information. To build an * NDC one uses the {@link #push push} operation. Simply put, * *

*

    *
  • Contexts can be nested. * *

    *

  • When entering a context, call NDC.push. As a side effect, if * there is no nested diagnostic context for the current thread, this method * will create it. * *

    *

  • When leaving a context, call NDC.pop. * *

    *

  • When exiting a thread make sure to call {@link #remove * NDC.remove()}. *
* *

* There is no penalty for forgetting to match each push operation * with a corresponding pop, except the obvious mismatch between * the real application context and the context set in the NDC. * *

* 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. * *

* 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. * *

* 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. * *

* This method is equivalent to calling the {@link #setMaxDepth} method with a * zero maxDepth 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. * *

* 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. * *

* 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. * *

* 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. * *

* The parent's diagnostic context is cloned before being inherited. In other * words, once inherited, the two diagnostic contexts can be managed * independently. * *

* 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); } /** * Never use this method directly, use the * {@link org.apache.log4j.spi.LoggingEvent#getNDC} method instead. */ 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. * *

* 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. * *

* 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. * *

* The contents of the message 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. * *

* Each thread that created a diagnostic context by calling {@link #push} should * call this method before exiting. Otherwise, the memory used by the * thread cannot be reclaimed by the VM. * *

* 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 remove 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 maxDepth, then no action is taken. * *

* 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 setMaxDepth method * circumvents this problem. * *

* For example, the combination * *

       void foo() {
          int depth = NDC.getDepth();
    
          ... complex sequence of calls
    
          NDC.setMaxDepth(depth);
       }
     * 
* * 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; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy