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 java.io.Serializable;
21  import java.util.AbstractCollection;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.NoSuchElementException;
28  
29  import org.apache.logging.log4j.message.ParameterizedMessage;
30  import org.apache.logging.log4j.spi.DefaultThreadContextMap;
31  import org.apache.logging.log4j.spi.DefaultThreadContextStack;
32  import org.apache.logging.log4j.spi.NoOpThreadContextMap;
33  import org.apache.logging.log4j.spi.ThreadContextMap;
34  import org.apache.logging.log4j.spi.ThreadContextMap2;
35  import org.apache.logging.log4j.spi.ThreadContextMapFactory;
36  import org.apache.logging.log4j.spi.ThreadContextStack;
37  import org.apache.logging.log4j.status.StatusLogger;
38  import org.apache.logging.log4j.util.PropertiesUtil;
39  
40  /**
41   * The ThreadContext allows applications to store information either in a Map or a Stack.
42   * <p>
43   * <b><em>The MDC is managed on a per thread basis</em></b>. A child thread automatically inherits a <em>copy</em> of
44   * the mapped diagnostic context of its parent.
45   * </p>
46   */
47  public final class ThreadContext {
48  
49      /**
50       * An empty read-only ThreadContextStack.
51       */
52      private static class EmptyThreadContextStack extends AbstractCollection<String> implements ThreadContextStack {
53  
54          private static final long serialVersionUID = 1L;
55  
56          private static final Iterator<String> EMPTY_ITERATOR = new EmptyIterator<>();
57  
58          @Override
59          public String pop() {
60              return null;
61          }
62  
63          @Override
64          public String peek() {
65              return null;
66          }
67  
68          @Override
69          public void push(final String message) {
70              throw new UnsupportedOperationException();
71          }
72  
73          @Override
74          public int getDepth() {
75              return 0;
76          }
77  
78          @Override
79          public List<String> asList() {
80              return Collections.emptyList();
81          }
82  
83          @Override
84          public void trim(final int depth) {
85              // Do nothing
86          }
87  
88          @Override
89          public boolean equals(final Object o) {
90              // Similar to java.util.Collections.EmptyList.equals(Object)
91              return (o instanceof Collection) && ((Collection<?>) o).isEmpty();
92          }
93  
94          @Override
95          public int hashCode() {
96              // Same as java.util.Collections.EmptyList.hashCode()
97              return 1;
98          }
99  
100         @Override
101         public ContextStack copy() {
102             return this;
103         }
104 
105         @Override
106         public <T> T[] toArray(final T[] a) {
107             throw new UnsupportedOperationException();
108         }
109 
110         @Override
111         public boolean add(final String e) {
112             throw new UnsupportedOperationException();
113         }
114 
115         @Override
116         public boolean containsAll(final Collection<?> c) {
117             return false;
118         }
119 
120         @Override
121         public boolean addAll(final Collection<? extends String> c) {
122             throw new UnsupportedOperationException();
123         }
124 
125         @Override
126         public boolean removeAll(final Collection<?> c) {
127             throw new UnsupportedOperationException();
128         }
129 
130         @Override
131         public boolean retainAll(final Collection<?> c) {
132             throw new UnsupportedOperationException();
133         }
134 
135         @Override
136         public Iterator<String> iterator() {
137             return EMPTY_ITERATOR;
138         }
139 
140         @Override
141         public int size() {
142             return 0;
143         }
144 
145         @Override
146         public ContextStack getImmutableStackOrNull() {
147             return this;
148         }
149     }
150 
151     /**
152      * An empty iterator. Since Java 1.7 added the Collections.emptyIterator() method, we have to make do.
153      *
154      * @param <E> the type of the empty iterator
155      */
156     private static class EmptyIterator<E> implements Iterator<E> {
157 
158         @Override
159         public boolean hasNext() {
160             return false;
161         }
162 
163         @Override
164         public E next() {
165             throw new NoSuchElementException("This is an empty iterator!");
166         }
167 
168         @Override
169         public void remove() {
170             // no-op
171         }
172     }
173 
174     /**
175      * Empty, immutable Map.
176      */
177     // ironically, this annotation gives an "unsupported @SuppressWarnings" warning in Eclipse
178     @SuppressWarnings("PublicStaticCollectionField")
179     // I like irony, so I won't delete it...
180     public static final Map<String, String> EMPTY_MAP = Collections.emptyMap();
181 
182     /**
183      * Empty, immutable ContextStack.
184      */
185     // ironically, this annotation gives an "unsupported @SuppressWarnings" warning in Eclipse
186     @SuppressWarnings("PublicStaticCollectionField")
187     public static final ThreadContextStack EMPTY_STACK = new EmptyThreadContextStack();
188 
189     private static final String DISABLE_MAP = "disableThreadContextMap";
190     private static final String DISABLE_STACK = "disableThreadContextStack";
191     private static final String DISABLE_ALL = "disableThreadContext";
192 
193     private static boolean disableAll;
194     private static boolean useMap;
195     private static boolean useStack;
196     private static ThreadContextMap contextMap;
197     private static ThreadContextStack contextStack;
198     private static final Logger LOGGER = StatusLogger.getLogger();
199 
200     static {
201         init();
202     }
203 
204     private ThreadContext() {
205         // empty
206     }
207 
208     /**
209      * <em>Consider private, used for testing.</em>
210      */
211     static void init() {
212         contextMap = null;
213         final PropertiesUtil managerProps = PropertiesUtil.getProperties();
214         disableAll = managerProps.getBooleanProperty(DISABLE_ALL);
215         useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || disableAll);
216         useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || disableAll);
217 
218         contextStack = new DefaultThreadContextStack(useStack);
219         if (!useMap) {
220             contextMap = new NoOpThreadContextMap();
221         } else {
222             contextMap = ThreadContextMapFactory.createThreadContextMap();
223         }
224     }
225 
226     /**
227      * Puts a context value (the <code>value</code> parameter) as identified with the <code>key</code> parameter into
228      * the current thread's context map.
229      *
230      * <p>
231      * If the current thread does not have a context map it is created as a side effect.
232      * </p>
233      *
234      * @param key The key name.
235      * @param value The key value.
236      */
237     public static void put(final String key, final String value) {
238         contextMap.put(key, value);
239     }
240 
241     /**
242      * Puts all given context map entries into the current thread's
243      * context map.
244      *
245      * <p>If the current thread does not have a context map it is
246      * created as a side effect.</p>
247      * @param m The map.
248      * @since 2.7
249      */
250     public static void putAll(final Map<String, String> m) {
251         if (contextMap instanceof ThreadContextMap2) {
252             ((ThreadContextMap2) contextMap).putAll(m);
253         } else if (contextMap instanceof DefaultThreadContextMap) {
254             ((DefaultThreadContextMap) contextMap).putAll(m);
255         } else {
256             for (final Map.Entry<String, String> entry: m.entrySet()) {
257                 contextMap.put(entry.getKey(), entry.getValue());
258             }
259         }
260     }
261 
262     /**
263      * Gets the context value identified by the <code>key</code> parameter.
264      *
265      * <p>
266      * This method has no side effects.
267      * </p>
268      *
269      * @param key The key to locate.
270      * @return The value associated with the key or null.
271      */
272     public static String get(final String key) {
273         return contextMap.get(key);
274     }
275 
276     /**
277      * Removes the context value identified by the <code>key</code> parameter.
278      *
279      * @param key The key to remove.
280      */
281     public static void remove(final String key) {
282         contextMap.remove(key);
283     }
284 
285     /**
286      * Clears the context map.
287      */
288     public static void clearMap() {
289         contextMap.clear();
290     }
291 
292     /**
293      * Clears the context map and stack.
294      */
295     public static void clearAll() {
296         clearMap();
297         clearStack();
298     }
299 
300     /**
301      * Determines if the key is in the context.
302      *
303      * @param key The key to locate.
304      * @return True if the key is in the context, false otherwise.
305      */
306     public static boolean containsKey(final String key) {
307         return contextMap.containsKey(key);
308     }
309 
310     /**
311      * Returns a mutable copy of current thread's context Map.
312      *
313      * @return a mutable copy of the context.
314      */
315     public static Map<String, String> getContext() {
316         return contextMap.getCopy();
317     }
318 
319     /**
320      * Returns an immutable view of the current thread's context Map.
321      *
322      * @return An immutable view of the ThreadContext Map.
323      */
324     public static Map<String, String> getImmutableContext() {
325         final Map<String, String> map = contextMap.getImmutableMapOrNull();
326         return map == null ? EMPTY_MAP : map;
327     }
328 
329     /**
330      * Returns the internal data structure used to store thread context key-value pairs.
331      * <p><em>
332      * This data structure is not intended to be used directly by applications. This method is package protected for
333      * internal log4j2 usage.
334      * </em></p>
335      * @return the internal data structure used to store thread context key-value pairs
336      */
337     static ThreadContextMap getThreadContextMap() {
338         return contextMap;
339     }
340 
341     /**
342      * Returns true if the Map is empty.
343      *
344      * @return true if the Map is empty, false otherwise.
345      */
346     public static boolean isEmpty() {
347         return contextMap.isEmpty();
348     }
349 
350     /**
351      * Clears the stack for this thread.
352      */
353     public static void clearStack() {
354         contextStack.clear();
355     }
356 
357     /**
358      * Returns a copy of this thread's stack.
359      *
360      * @return A copy of this thread's stack.
361      */
362     public static ContextStack cloneStack() {
363         return contextStack.copy();
364     }
365 
366     /**
367      * Gets an immutable copy of this current thread's context stack.
368      *
369      * @return an immutable copy of the ThreadContext stack.
370      */
371     public static ContextStack getImmutableStack() {
372         final ContextStack result = contextStack.getImmutableStackOrNull();
373         return result == null ? EMPTY_STACK : result;
374     }
375 
376     /**
377      * Sets this thread's stack.
378      *
379      * @param stack The stack to use.
380      */
381     public static void setStack(final Collection<String> stack) {
382         if (stack.isEmpty() || !useStack) {
383             return;
384         }
385         contextStack.clear();
386         contextStack.addAll(stack);
387     }
388 
389     /**
390      * Gets the current nesting depth of this thread's stack.
391      *
392      * @return the number of items in the stack.
393      *
394      * @see #trim
395      */
396     public static int getDepth() {
397         return contextStack.getDepth();
398     }
399 
400     /**
401      * Returns the value of the last item placed on the stack.
402      *
403      * <p>
404      * The returned value is the value that was pushed last. If no context is available, then the empty string "" is
405      * returned.
406      * </p>
407      *
408      * @return String The innermost diagnostic context.
409      */
410     public static String pop() {
411         return contextStack.pop();
412     }
413 
414     /**
415      * Looks at the last diagnostic context at the top of this NDC without removing it.
416      *
417      * <p>
418      * The returned value is the value that was pushed last. If no context is available, then the empty string "" is
419      * returned.
420      * </p>
421      *
422      * @return String The innermost diagnostic context.
423      */
424     public static String peek() {
425         return contextStack.peek();
426     }
427 
428     /**
429      * Pushes new diagnostic context information for the current thread.
430      *
431      * <p>
432      * The contents of the <code>message</code> parameter is determined solely by the client.
433      * </p>
434      *
435      * @param message The new diagnostic context information.
436      */
437     public static void push(final String message) {
438         contextStack.push(message);
439     }
440 
441     /**
442      * Pushes new diagnostic context information for the current thread.
443      *
444      * <p>
445      * The contents of the <code>message</code> and args parameters are determined solely by the client. The message
446      * will be treated as a format String and tokens will be replaced with the String value of the arguments in
447      * accordance with ParameterizedMessage.
448      * </p>
449      *
450      * @param message The new diagnostic context information.
451      * @param args Parameters for the message.
452      */
453     public static void push(final String message, final Object... args) {
454         contextStack.push(ParameterizedMessage.format(message, args));
455     }
456 
457     /**
458      * Removes the diagnostic context for this thread.
459      *
460      * <p>
461      * Each thread that created a diagnostic context by calling {@link #push} should call this method before exiting.
462      * Otherwise, the memory used by the <b>thread</b> cannot be reclaimed by the VM.
463      * </p>
464      *
465      * <p>
466      * As this is such an important problem in heavy duty systems and because it is difficult to always guarantee that
467      * the remove method is called before exiting a thread, this method has been augmented to lazily remove references
468      * to dead threads. In practice, this means that you can be a little sloppy and occasionally forget to call
469      * {@link #remove} before exiting a thread. However, you must call <code>remove</code> sometime. If you never call
470      * it, then your application is sure to run out of memory.
471      * </p>
472      */
473     public static void removeStack() {
474         contextStack.clear();
475     }
476 
477     /**
478      * Trims elements from this diagnostic context. If the current depth is smaller or equal to <code>maxDepth</code>,
479      * then no action is taken. If the current depth is larger than newDepth then all elements at maxDepth or higher are
480      * discarded.
481      *
482      * <p>
483      * This method is a convenient alternative to multiple {@link #pop} calls. Moreover, it is often the case that at
484      * the end of complex call sequences, the depth of the ThreadContext is unpredictable. The <code>trim</code> method
485      * circumvents this problem.
486      * </p>
487      *
488      * <p>
489      * For example, the combination
490      * </p>
491      *
492      * <pre>
493      * void foo() {
494      *     final int depth = ThreadContext.getDepth();
495      *
496      *     // ... complex sequence of calls
497      *
498      *     ThreadContext.trim(depth);
499      * }
500      * </pre>
501      *
502      * <p>
503      * ensures that between the entry and exit of {@code foo} the depth of the diagnostic stack is conserved.
504      * </p>
505      *
506      * @see #getDepth
507      * @param depth The number of elements to keep.
508      */
509     public static void trim(final int depth) {
510         contextStack.trim(depth);
511     }
512 
513     /**
514      * The ThreadContext Stack interface.
515      */
516     public interface ContextStack extends Serializable, Collection<String> {
517 
518         /**
519          * Returns the element at the top of the stack.
520          *
521          * @return The element at the top of the stack.
522          * @throws java.util.NoSuchElementException if the stack is empty.
523          */
524         String pop();
525 
526         /**
527          * Returns the element at the top of the stack without removing it or null if the stack is empty.
528          *
529          * @return the element at the top of the stack or null if the stack is empty.
530          */
531         String peek();
532 
533         /**
534          * Pushes an element onto the stack.
535          *
536          * @param message The element to add.
537          */
538         void push(String message);
539 
540         /**
541          * Returns the number of elements in the stack.
542          *
543          * @return the number of elements in the stack.
544          */
545         int getDepth();
546 
547         /**
548          * Returns all the elements in the stack in a List.
549          *
550          * @return all the elements in the stack in a List.
551          */
552         List<String> asList();
553 
554         /**
555          * Trims elements from the end of the stack.
556          *
557          * @param depth The maximum number of items in the stack to keep.
558          */
559         void trim(int depth);
560 
561         /**
562          * Returns a copy of the ContextStack.
563          *
564          * @return a copy of the ContextStack.
565          */
566         ContextStack copy();
567 
568         /**
569          * Returns a ContextStack with the same contents as this ContextStack or {@code null}. Attempts to modify the
570          * returned stack may or may not throw an exception, but will not affect the contents of this ContextStack.
571          *
572          * @return a ContextStack with the same contents as this ContextStack or {@code null}.
573          */
574         ContextStack getImmutableStackOrNull();
575     }
576 }