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