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