KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > log4j > NDC


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 // Contributors: Dan Milstein
18
// Ray Millard
19

20 package org.apache.log4j;
21
22 import java.util.Hashtable JavaDoc;
23 import java.util.Stack JavaDoc;
24 import java.util.Enumeration JavaDoc;
25 import java.util.Vector JavaDoc;
26
27 import org.apache.log4j.helpers.LogLog;
28
29 /**
30    The NDC class implements <i>nested diagnostic contexts</i> as
31    defined by Neil Harrison in the article "Patterns for Logging
32    Diagnostic Messages" part of the book "<i>Pattern Languages of
33    Program Design 3</i>" edited by Martin et al.
34
35    <p>A Nested Diagnostic Context, or NDC in short, is an instrument
36    to distinguish interleaved log output from different sources. Log
37    output is typically interleaved when a server handles multiple
38    clients near-simultaneously.
39
40    <p>Interleaved log output can still be meaningful if each log entry
41    from different contexts had a distinctive stamp. This is where NDCs
42    come into play.
43
44    <p><em><b>Note that NDCs are managed on a per thread
45    basis</b></em>. NDC operations such as {@link #push push}, {@link
46    #pop}, {@link #clear}, {@link #getDepth} and {@link #setMaxDepth}
47    affect the NDC of the <em>current</em> thread only. NDCs of other
48    threads remain unaffected.
49
50    <p>For example, a servlet can build a per client request NDC
51    consisting the clients host name and other information contained in
52    the the request. <em>Cookies</em> are another source of distinctive
53    information. To build an NDC one uses the {@link #push push}
54    operation. Simply put,
55
56    <p><ul>
57      <li>Contexts can be nested.
58
59      <p><li>When entering a context, call <code>NDC.push</code>. As a
60      side effect, if there is no nested diagnostic context for the
61      current thread, this method will create it.
62
63      <p><li>When leaving a context, call <code>NDC.pop</code>.
64
65      <p><li><b>When exiting a thread make sure to call {@link #remove
66      NDC.remove()}</b>.
67    </ul>
68    
69    <p>There is no penalty for forgetting to match each
70    <code>push</code> operation with a corresponding <code>pop</code>,
71    except the obvious mismatch between the real application context
72    and the context set in the NDC.
73
74    <p>If configured to do so, {@link PatternLayout} and {@link
75    TTCCLayout} instances automatically retrieve the nested diagnostic
76    context for the current thread without any user intervention.
77    Hence, even if a servlet is serving multiple clients
78    simultaneously, the logs emanating from the same code (belonging to
79    the same category) can still be distinguished because each client
80    request will have a different NDC tag.
81
82    <p>Heavy duty systems should call the {@link #remove} method when
83    leaving the run method of a thread. This ensures that the memory
84    used by the thread can be freed by the Java garbage
85    collector. There is a mechanism to lazily remove references to dead
86    threads. In practice, this means that you can be a little sloppy
87    and sometimes forget to call {@link #remove} before exiting a
88    thread.
89    
90    <p>A thread may inherit the nested diagnostic context of another
91    (possibly parent) thread using the {@link #inherit inherit}
92    method. A thread may obtain a copy of its NDC with the {@link
93    #cloneStack cloneStack} method and pass the reference to any other
94    thread, in particular to a child.
95    
96    @author Ceki G&uuml;lc&uuml;
97    @since 0.7.0
98   
99 */

100  
101 public class NDC {
102
103   // The synchronized keyword is not used in this class. This may seem
104
// dangerous, especially since the class will be used by
105
// multiple-threads. In particular, all threads share the same
106
// hashtable (the "ht" variable). This is OK since java hashtables
107
// are thread safe. Same goes for Stacks.
108

109   // More importantly, when inheriting diagnostic contexts the child
110
// thread is handed a clone of the parent's NDC. It follows that
111
// each thread has its own NDC (i.e. stack).
112

113   static Hashtable JavaDoc ht = new Hashtable JavaDoc();
114
115   static int pushCounter = 0; // the number of times push has been called
116
// after the latest call to lazyRemove
117

118   // The number of times we allow push to be called before we call lazyRemove
119
// 5 is a relatively small number. As such, lazyRemove is not called too
120
// frequently. We thus avoid the cost of creating an Enumeration too often.
121
// The higher this number, the longer is the avarage period for which all
122
// logging calls in all threads are blocked.
123
static final int REAP_THRESHOLD = 5;
124   
125   // No instances allowed.
126
private NDC() {}
127
128
129   /**
130      Clear any nested diagnostic information if any. This method is
131      useful in cases where the same thread can be potentially used
132      over and over in different unrelated contexts.
133
134      <p>This method is equivalent to calling the {@link #setMaxDepth}
135      method with a zero <code>maxDepth</code> argument.
136      
137      @since 0.8.4c */

138   public
139   static
140   void clear() {
141     Stack JavaDoc stack = (Stack JavaDoc) ht.get(Thread.currentThread());
142     if(stack != null)
143       stack.setSize(0);
144   }
145
146   
147   /**
148      Clone the diagnostic context for the current thread.
149
150      <p>Internally a diagnostic context is represented as a stack. A
151      given thread can supply the stack (i.e. diagnostic context) to a
152      child thread so that the child can inherit the parent thread's
153      diagnostic context.
154
155      <p>The child thread uses the {@link #inherit inherit} method to
156      inherit the parent's diagnostic context.
157      
158      @return Stack A clone of the current thread's diagnostic context.
159
160   */

161   public
162   static
163   Stack JavaDoc cloneStack() {
164     Object JavaDoc o = ht.get(Thread.currentThread());
165     if(o == null)
166       return null;
167     else {
168       Stack JavaDoc stack = (Stack JavaDoc) o;
169       return (Stack JavaDoc) stack.clone();
170     }
171   }
172
173   
174   /**
175      Inherit the diagnostic context of another thread.
176
177      <p>The parent thread can obtain a reference to its diagnostic
178      context using the {@link #cloneStack} method. It should
179      communicate this information to its child so that it may inherit
180      the parent's diagnostic context.
181
182      <p>The parent's diagnostic context is cloned before being
183      inherited. In other words, once inherited, the two diagnostic
184      contexts can be managed independently.
185      
186      <p>In java, a child thread cannot obtain a reference to its
187      parent, unless it is directly handed the reference. Consequently,
188      there is no client-transparent way of inheriting diagnostic
189      contexts. Do you know any solution to this problem?
190
191      @param stack The diagnostic context of the parent thread.
192
193   */

194   public
195   static
196   void inherit(Stack JavaDoc stack) {
197     if(stack != null)
198       ht.put(Thread.currentThread(), stack);
199   }
200
201
202   /**
203      <font color="#FF4040"><b>Never use this method directly, use the {@link
204      org.apache.log4j.spi.LoggingEvent#getNDC} method instead</b></font>.
205   */

206   static
207   public
208   String JavaDoc get() {
209     Stack JavaDoc s = (Stack JavaDoc) ht.get(Thread.currentThread());
210     if(s != null && !s.isEmpty())
211       return ((DiagnosticContext) s.peek()).fullMessage;
212     else
213       return null;
214   }
215   
216   /**
217    * Get the current nesting depth of this diagnostic context.
218    *
219    * @see #setMaxDepth
220    * @since 0.7.5
221    */

222   public
223   static
224   int getDepth() {
225     Stack JavaDoc stack = (Stack JavaDoc) ht.get(Thread.currentThread());
226     if(stack == null)
227       return 0;
228     else
229       return stack.size();
230   }
231
232   private
233   static
234   void lazyRemove() {
235      
236     // The synchronization on ht is necessary to prevent JDK 1.2.x from
237
// throwing ConcurrentModificationExceptions at us. This sucks BIG-TIME.
238
// One solution is to write our own hashtable implementation.
239
Vector JavaDoc v;
240     
241     synchronized(ht) {
242       // Avoid calling clean-up too often.
243
if(++pushCounter <= REAP_THRESHOLD) {
244     return; // We release the lock ASAP.
245
} else {
246     pushCounter = 0; // OK let's do some work.
247
}
248
249       int misses = 0;
250       v = new Vector JavaDoc();
251       Enumeration JavaDoc enumeration = ht.keys();
252       // We give up after 4 straigt missses. That is 4 consecutive
253
// inspected threads in 'ht' that turn out to be alive.
254
// The higher the proportion on dead threads in ht, the higher the
255
// chances of removal.
256
while(enumeration.hasMoreElements() && (misses <= 4)) {
257     Thread JavaDoc t = (Thread JavaDoc) enumeration.nextElement();
258     if(t.isAlive()) {
259       misses++;
260     } else {
261       misses = 0;
262       v.addElement(t);
263     }
264       }
265     } // synchronized
266

267     int size = v.size();
268     for(int i = 0; i < size; i++) {
269       Thread JavaDoc t = (Thread JavaDoc) v.elementAt(i);
270       LogLog.debug("Lazy NDC removal for thread [" + t.getName() + "] ("+
271            ht.size() + ").");
272       ht.remove(t);
273     }
274   }
275
276   /**
277      Clients should call this method before leaving a diagnostic
278      context.
279
280      <p>The returned value is the value that was pushed last. If no
281      context is available, then the empty string "" is returned.
282      
283      @return String The innermost diagnostic context.
284      
285      */

286   public
287   static
288   String JavaDoc pop() {
289     Thread JavaDoc key = Thread.currentThread();
290     Stack JavaDoc stack = (Stack JavaDoc) ht.get(key);
291     if(stack != null && !stack.isEmpty())
292       return ((DiagnosticContext) stack.pop()).message;
293     else
294       return "";
295   }
296
297   /**
298      Looks at the last diagnostic context at the top of this NDC
299      without removing it.
300
301      <p>The returned value is the value that was pushed last. If no
302      context is available, then the empty string "" is returned.
303      
304      @return String The innermost diagnostic context.
305      
306      */

307   public
308   static
309   String JavaDoc peek() {
310     Thread JavaDoc key = Thread.currentThread();
311     Stack JavaDoc stack = (Stack JavaDoc) ht.get(key);
312     if(stack != null && !stack.isEmpty())
313       return ((DiagnosticContext) stack.peek()).message;
314     else
315       return "";
316   }
317   
318   /**
319      Push new diagnostic context information for the current thread.
320
321      <p>The contents of the <code>message</code> parameter is
322      determined solely by the client.
323      
324      @param message The new diagnostic context information. */

325   public
326   static
327   void push(String JavaDoc message) {
328     Thread JavaDoc key = Thread.currentThread();
329     Stack JavaDoc stack = (Stack JavaDoc) ht.get(key);
330       
331     if(stack == null) {
332       DiagnosticContext dc = new DiagnosticContext(message, null);
333       stack = new Stack JavaDoc();
334       ht.put(key, stack);
335       stack.push(dc);
336     } else if (stack.isEmpty()) {
337       DiagnosticContext dc = new DiagnosticContext(message, null);
338       stack.push(dc);
339     } else {
340       DiagnosticContext parent = (DiagnosticContext) stack.peek();
341       stack.push(new DiagnosticContext(message, parent));
342     }
343   }
344
345   /**
346      Remove the diagnostic context for this thread.
347
348      <p>Each thread that created a diagnostic context by calling
349      {@link #push} should call this method before exiting. Otherwise,
350      the memory used by the <b>thread</b> cannot be reclaimed by the
351      VM.
352
353      <p>As this is such an important problem in heavy duty systems and
354      because it is difficult to always guarantee that the remove
355      method is called before exiting a thread, this method has been
356      augmented to lazily remove references to dead threads. In
357      practice, this means that you can be a little sloppy and
358      occasionally forget to call {@link #remove} before exiting a
359      thread. However, you must call <code>remove</code> sometime. If
360      you never call it, then your application is sure to run out of
361      memory.
362      
363   */

364   static
365   public
366   void remove() {
367     ht.remove(Thread.currentThread());
368     
369     // Lazily remove dead-thread references in ht.
370
lazyRemove();
371   }
372
373   /**
374      Set maximum depth of this diagnostic context. If the current
375      depth is smaller or equal to <code>maxDepth</code>, then no
376      action is taken.
377
378      <p>This method is a convenient alternative to multiple {@link
379      #pop} calls. Moreover, it is often the case that at the end of
380      complex call sequences, the depth of the NDC is
381      unpredictable. The <code>setMaxDepth</code> method circumvents
382      this problem.
383
384      <p>For example, the combination
385      <pre>
386        void foo() {
387        &nbsp; int depth = NDC.getDepth();
388
389        &nbsp; ... complex sequence of calls
390
391        &nbsp; NDC.setMaxDepth(depth);
392        }
393      </pre>
394
395      ensures that between the entry and exit of foo the depth of the
396      diagnostic stack is conserved.
397      
398      @see #getDepth
399      @since 0.7.5 */

400   static
401   public
402   void setMaxDepth(int maxDepth) {
403     Stack JavaDoc stack = (Stack JavaDoc) ht.get(Thread.currentThread());
404     if(stack != null && maxDepth < stack.size())
405       stack.setSize(maxDepth);
406   }
407   
408   // =====================================================================
409
private static class DiagnosticContext {
410
411     String JavaDoc fullMessage;
412     String JavaDoc message;
413     
414     DiagnosticContext(String JavaDoc message, DiagnosticContext parent) {
415       this.message = message;
416       if(parent != null) {
417     fullMessage = parent.fullMessage + ' ' + message;
418       } else {
419     fullMessage = message;
420       }
421     }
422   }
423 }
424
425
Popular Tags