View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  
18  package org.apache.logging.log4j;
19  
20  import org.apache.logging.log4j.message.ParameterizedMessage;
21  
22  import java.io.Serializable;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.NoSuchElementException;
29  
30  /**
31   * The ThreadContext allows applications to store information either in a Map
32   * <p/>
33   * <p><b><em>The MDC is managed on a per thread basis</em></b>. A
34   * child thread automatically inherits a <em>copy</em> of the mapped
35   * diagnostic context of its parent.
36   */
37  public final class ThreadContext {
38  
39      /**
40       * Empty, immutable Map.
41       */
42      public static final Map<String, String> EMPTY_MAP = new ImmutableMap();
43  
44      /**
45       * Empty, immutable ContextStack.
46       */
47      public static final ContextStack EMPTY_STACK = new ImmutableStack();
48  
49      private static final String DISABLE_MAP = "disableThreadContextMap";
50  
51      private static final String DISABLE_STACK = "disableThreadContextStack";
52  
53      private static final String DISABLE_ALL = "disableThreadContext";
54  
55      private static boolean all = Boolean.getBoolean(DISABLE_ALL);
56  
57      private static boolean useMap = !(Boolean.getBoolean(DISABLE_MAP) || all);
58  
59      private static boolean useStack = !(Boolean.getBoolean(DISABLE_STACK) || all);
60  
61      private static ThreadLocal<Map<String, String>> localMap =
62          new InheritableThreadLocal<Map<String, String>>() {
63              @Override
64              protected Map<String, String> childValue(Map<String, String> parentValue) {
65                  return parentValue == null || !useMap ? null : new HashMap<String, String>(parentValue);
66              }
67          };
68  
69      private static ThreadLocal<ContextStack> localStack = new ThreadLocal<ContextStack>();
70  
71      private ThreadContext() {
72  
73      }
74  
75      /**
76       * Put a context value (the <code>o</code> parameter) as identified
77       * with the <code>key</code> parameter into the current thread's
78       * context map.
79       * <p/>
80       * <p>If the current thread does not have a context map it is
81       * created as a side effect.
82       * @param key The key name.
83       * @param value The key value.
84       */
85      public static void put(String key, String value) {
86          if (!useMap) {
87              return;
88          }
89          Map<String, String> map = localMap.get();
90          if (map == null) {
91              map = new HashMap<String, String>();
92              localMap.set(map);
93          }
94          map.put(key, value);
95      }
96  
97      /**
98       * Get the context identified by the <code>key</code> parameter.
99       * <p/>
100      * <p>This method has no side effects.
101      * @param key The key to locate.
102      * @return The value associated with the key or null.
103      */
104     public static String get(String key) {
105         Map<String, String> map = localMap.get();
106         return map == null ? null : map.get(key);
107     }
108 
109     /**
110      * Remove the the context identified by the <code>key</code>
111      * parameter.
112      * @param key The key to remove.
113      */
114     public static void remove(String key) {
115         Map<String, String> map = localMap.get();
116         if (map != null) {
117             map.remove(key);
118         }
119     }
120 
121     /**
122      * Clear the context.
123      */
124     public static void clear() {
125         localMap.remove();
126     }
127 
128     /**
129      * Determine if the key is in the context.
130      * @param key The key to locate.
131      * @return True if the key is in the context, false otherwise.
132      */
133     public static boolean containsKey(String key) {
134         Map<String, String> map = localMap.get();
135         return map == null ? false : map.containsKey(key);
136     }
137 
138     /**
139      * Get a copy of current thread's context Map.
140      * @return a copy of the context.
141      */
142     public static Map<String, String> getContext() {
143         Map<String, String> map = localMap.get();
144         return map == null ? new HashMap<String, String>() : new HashMap<String, String>(map);
145     }
146 
147     /**
148      * Get an immutable copy of the current thread's context Map.
149      * @return An immutable copy of the ThreadContext Map.
150      */
151     public static Map<String, String> getImmutableContext() {
152         Map<String, String> map = localMap.get();
153         return map == null ? new ImmutableMap() : new ImmutableMap(map);
154     }
155 
156     /**
157      * Returns true if the Map is empty.
158      * @return true if the Map is empty, false otherwise.
159      */
160     public static boolean isEmpty() {
161         Map<String, String> map = localMap.get();
162         return map == null || map.size() == 0;
163     }
164 
165     /**
166      * Clear the stack for this thread.
167      */
168     public static void clearStack() {
169         localStack.remove();
170     }
171 
172     /**
173      * Returns a copy of this thread's stack.
174      * @return A copy of this thread's stack.
175      */
176     public static ContextStack cloneStack() {
177         ContextStack stack = localStack.get();
178         return stack == null ? new ThreadContextStack() : new ThreadContextStack(stack.asList());
179     }
180 
181     /**
182      * Get an immutable copy of this current thread's context stack.
183      * @return an immutable copy of the ThreadContext stack.
184      */
185     public static ContextStack getImmutableStack() {
186         ContextStack stack = localStack.get();
187         return stack == null ? EMPTY_STACK : new ImmutableStack(stack.asList());
188     }
189 
190     /**
191      * Set this thread's stack.
192      * @param stack The stack to use.
193      */
194     public static void setStack(Collection<String> stack) {
195         if (stack.size() == 0 || !useStack) {
196             return;
197         }
198         localStack.set(new ThreadContextStack(stack));
199     }
200 
201     /**
202      * Get the current nesting depth of this thread's stack.
203      * @return the number of items in the stack.
204      *
205      * @see #trim
206      */
207     public static int getDepth() {
208         ContextStack stack = localStack.get();
209         return stack == null ? 0 : stack.getDepth();
210     }
211 
212     /**
213      * Returns the value of the last item placed on the stack.
214      * <p/>
215      * <p>The returned value is the value that was pushed last. If no
216      * context is available, then the empty string "" is returned.
217      *
218      * @return String The innermost diagnostic context.
219      */
220     public static String pop() {
221         ContextStack s = localStack.get();
222         if (s == null || s.getDepth() == 0) {
223             return "";
224         }
225         return s.pop();
226     }
227 
228     /**
229      * Looks at the last diagnostic context at the top of this NDC
230      * without removing it.
231      * <p/>
232      * <p>The returned value is the value that was pushed last. If no
233      * context is available, then the empty string "" is returned.
234      *
235      * @return String The innermost diagnostic context.
236      */
237     public static String peek() {
238         ContextStack s = localStack.get();
239         if (s == null || s.getDepth() == 0) {
240             return "";
241         }
242         return s.peek();
243     }
244 
245     /**
246      * Push new diagnostic context information for the current thread.
247      * <p/>
248      * <p>The contents of the <code>message</code> parameter is
249      * determined solely by the client.
250      *
251      * @param message The new diagnostic context information.
252      */
253     public static void push(String message) {
254         if (!useStack) {
255             return;
256         }
257         ContextStack stack = localStack.get();
258         if (stack == null) {
259             stack = new ThreadContextStack();
260             localStack.set(stack);
261         }
262         stack.push(message);
263     }
264     /**
265      * Push new diagnostic context information for the current thread.
266      * <p/>
267      * <p>The contents of the <code>message</code> and args parameters are
268      * determined solely by the client. The message will be treated as a format String
269      * and tokens will be replaced with the String value of the arguments in accordance
270      * with ParameterizedMessage.
271      *
272      * @param message The new diagnostic context information.
273      */
274     public static void push(String message, Object... args) {
275         if (!useStack) {
276             return;
277         }
278         ContextStack stack = localStack.get();
279         if (stack == null) {
280             stack = new ThreadContextStack();
281             localStack.set(stack);
282         }
283         stack.push(ParameterizedMessage.format(message, args));
284     }
285 
286     /**
287      * Remove the diagnostic context for this thread.
288      * <p/>
289      * <p>Each thread that created a diagnostic context by calling
290      * {@link #push} should call this method before exiting. Otherwise,
291      * the memory used by the <b>thread</b> cannot be reclaimed by the
292      * VM.
293      * <p/>
294      * <p>As this is such an important problem in heavy duty systems and
295      * because it is difficult to always guarantee that the remove
296      * method is called before exiting a thread, this method has been
297      * augmented to lazily remove references to dead threads. In
298      * practice, this means that you can be a little sloppy and
299      * occasionally forget to call {@link #remove} before exiting a
300      * thread. However, you must call <code>remove</code> sometime. If
301      * you never call it, then your application is sure to run out of
302      * memory.
303      */
304     public static void removeStack() {
305         localStack.remove();
306     }
307 
308     /**
309      * Trims elements from this diagnostic context. If the current
310      * depth is smaller or equal to <code>maxDepth</code>, then no
311      * action is taken. If the current depth is larger than newDepth
312      * then all elements at maxDepth or higher are discarded.
313      * <p/>
314      * <p>This method is a convenient alternative to multiple {@link
315      * #pop} calls. Moreover, it is often the case that at the end of
316      * complex call sequences, the depth of the ThreadContext is
317      * unpredictable. The <code>trim</code> method circumvents
318      * this problem.
319      * <p/>
320      * <p>For example, the combination
321      * <pre>
322      * void foo() {
323      * &nbsp;  int depth = ThreadContext.getDepth();
324      * <p/>
325      * &nbsp;  ... complex sequence of calls
326      * <p/>
327      * &nbsp;  ThreadContext.trim(depth);
328      * }
329      * </pre>
330      * <p/>
331      * ensures that between the entry and exit of foo the depth of the
332      * diagnostic stack is conserved.
333      *
334      * @see #getDepth
335      * @param depth The number of elements to keep.
336      */
337     public static void trim(int depth) {
338         ContextStack stack = localStack.get();
339         if (stack != null) {
340             stack.trim(depth);
341         }
342     }
343 
344     /**
345      * The ThreadContext Stack interface.
346      */
347     public interface ContextStack extends Serializable {
348 
349         /**
350          * Clears all elements from the stack.
351          */
352         void clear();
353 
354         /**
355          * Returns the element at the top of the stack.
356          * @return The element at the top of the stack.
357          * @throws java.util.NoSuchElementException if the stack is empty.
358          */
359         String pop();
360 
361         /**
362          * Returns the element at the top of the stack without removing it or null if the stack is empty.
363          * @return the element at the top of the stack or null if the stack is empty.
364          */
365         String peek();
366 
367         /**
368          * Add an element to the stack.
369          * @param message The element to add.
370          */
371         void push(String message);
372 
373         /**
374          * Returns the number of elements in the stack.
375          * @return the number of elements in the stack.
376          */
377         int getDepth();
378 
379         /**
380          * Returns all the elements in the stack in a List.
381          * @return all the elements in the stack in a List.
382          */
383         List<String> asList();
384 
385         /**
386          * Trims elements from the end of the stack.
387          * @param depth The maximum number of items in the stack to keep.
388          */
389         void trim(int depth);
390 
391         /**
392          * Returns a copy of the ContextStack.
393          * @return a copy of the ContextStack.
394          */
395         ContextStack copy();
396     }
397 
398     /**
399      * The ContextStack implementation.
400      */
401     private static class ThreadContextStack extends ArrayList<String> implements ContextStack {
402 
403         private static final long serialVersionUID = 5050501L;
404 
405         public ThreadContextStack() {
406             super();
407         }
408 
409         public ThreadContextStack(Collection<String> collection) {
410             super(collection);
411         }
412 
413         public String pop() {
414             int index = size() - 1;
415             if (index >= 0) {
416                 String result = get(index);
417                 remove(index);
418                 return result;
419             }
420             throw new NoSuchElementException("The ThreadContext stack is empty");
421         }
422 
423         public String peek() {
424             int index = size() - 1;
425             if (index >= 0) {
426                 return get(index);
427             }
428             return null;
429         }
430 
431         public void push(String message) {
432             add(message);
433         }
434 
435         public int getDepth() {
436             return size();
437         }
438 
439         public List<String> asList() {
440             return this;
441         }
442 
443         public void trim(int depth) {
444             if (depth < 0) {
445                 throw new IllegalArgumentException("Maximum stack depth cannot be negative");
446             }
447             while (size() > depth) {
448                 remove(size() - 1);
449             }
450 
451         }
452 
453         public ContextStack copy() {
454             return new ThreadContextStack(this);
455         }
456     }
457 
458     /**
459      * An immutable ContextStack.
460      */
461     public static class ImmutableStack extends ThreadContextStack {
462 
463         private static final long serialVersionUID = 5050502L;
464 
465         public ImmutableStack() {
466         }
467 
468         public ImmutableStack(Collection<String> collection) {
469             super(collection);
470         }
471 
472         public ImmutableStack(ThreadContextStack stack) {
473             super(stack);
474         }
475 
476         @Override
477         public void push(String message) {
478             throw new UnsupportedOperationException("Stack cannot be modified");
479         }
480 
481         @Override
482         public void trim(int depth) {
483             throw new UnsupportedOperationException("Stack cannot be modified");
484         }
485     }
486 
487     /**
488      * An immutable Context Map.
489      */
490     public static class ImmutableMap extends HashMap<String, String> {
491         private static final long serialVersionUID = 5050503L;
492 
493         public ImmutableMap() {
494             super();
495         }
496 
497         public ImmutableMap(Map<String, String> map) {
498             super(map);
499         }
500 
501         @Override
502         public String put(String s, String s1) {
503             throw new UnsupportedOperationException("Map cannot be modified");
504         }
505 
506         @Override
507         public void putAll(Map<? extends String, ? extends String> map) {
508             throw new UnsupportedOperationException("Map cannot be modified");
509         }
510 
511         @Override
512         public String remove(Object o) {
513             throw new UnsupportedOperationException("Map cannot be modified");
514         }
515 
516         @Override
517         public void clear() {
518             throw new UnsupportedOperationException("Map cannot be modified");
519         }
520     }
521 }