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         contextMap = null;
215         final PropertiesUtil managerProps = PropertiesUtil.getProperties();
216         disableAll = managerProps.getBooleanProperty(DISABLE_ALL);
217         useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || disableAll);
218         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         }
229     }
230 
231     /**
232      * Puts a context value (the <code>value</code> parameter) as identified with the <code>key</code> parameter into
233      * the current thread's context map.
234      *
235      * <p>
236      * If the current thread does not have a context map it is created as a side effect.
237      * </p>
238      *
239      * @param key The key name.
240      * @param value The key value.
241      */
242     public static void put(final String key, final String value) {
243         contextMap.put(key, value);
244     }
245 
246     /**
247      * Puts all given context map entries into the current thread's
248      * context map.
249      *
250      * <p>If the current thread does not have a context map it is
251      * created as a side effect.</p>
252      * @param m The map.
253      * @since 2.7
254      */
255     public static void putAll(final Map<String, String> m) {
256         if (contextMap instanceof ThreadContextMap2) {
257             ((ThreadContextMap2) contextMap).putAll(m);
258         } else if (contextMap instanceof DefaultThreadContextMap) {
259             ((DefaultThreadContextMap) contextMap).putAll(m);
260         } else {
261             for (final Map.Entry<String, String> entry: m.entrySet()) {
262                 contextMap.put(entry.getKey(), entry.getValue());
263             }
264         }
265     }
266 
267     /**
268      * Gets the context value identified by the <code>key</code> parameter.
269      *
270      * <p>
271      * This method has no side effects.
272      * </p>
273      *
274      * @param key The key to locate.
275      * @return The value associated with the key or null.
276      */
277     public static String get(final String key) {
278         return contextMap.get(key);
279     }
280 
281     /**
282      * Removes the context value identified by the <code>key</code> parameter.
283      *
284      * @param key The key to remove.
285      */
286     public static void remove(final String key) {
287         contextMap.remove(key);
288     }
289 
290     /**
291      * Removes the context values identified by the <code>keys</code> parameter.
292      *
293      * @param keys The keys to remove.
294      *
295      * @since 2.8
296      */
297     public static void removeAll(final Iterable<String> keys) {
298         if (contextMap instanceof CleanableThreadContextMap) {
299             ((CleanableThreadContextMap) contextMap).removeAll(keys);
300         } else if (contextMap instanceof DefaultThreadContextMap) {
301             ((DefaultThreadContextMap) contextMap).removeAll(keys);
302         } else {
303             for (final String key : keys) {
304                 contextMap.remove(key);
305             }
306         }
307     }
308 
309     /**
310      * Clears the context map.
311      */
312     public static void clearMap() {
313         contextMap.clear();
314     }
315 
316     /**
317      * Clears the context map and stack.
318      */
319     public static void clearAll() {
320         clearMap();
321         clearStack();
322     }
323 
324     /**
325      * Determines if the key is in the context.
326      *
327      * @param key The key to locate.
328      * @return True if the key is in the context, false otherwise.
329      */
330     public static boolean containsKey(final String key) {
331         return contextMap.containsKey(key);
332     }
333 
334     /**
335      * Returns a mutable copy of current thread's context Map.
336      *
337      * @return a mutable copy of the context.
338      */
339     public static Map<String, String> getContext() {
340         return contextMap.getCopy();
341     }
342 
343     /**
344      * Returns an immutable view of the current thread's context Map.
345      *
346      * @return An immutable view of the ThreadContext Map.
347      */
348     public static Map<String, String> getImmutableContext() {
349         final Map<String, String> map = contextMap.getImmutableMapOrNull();
350         return map == null ? EMPTY_MAP : map;
351     }
352 
353     /**
354      * Returns a read-only view of the internal data structure used to store thread context key-value pairs,
355      * or {@code null} if the internal data structure does not implement the
356      * {@code ReadOnlyThreadContextMap} interface.
357      * <p>
358      * The {@link DefaultThreadContextMap} implementation does not implement {@code ReadOnlyThreadContextMap}, so by
359      * default this method returns {@code null}.
360      * </p>
361      *
362      * @return the internal data structure used to store thread context key-value pairs or {@code null}
363      * @see ThreadContextMapFactory
364      * @see DefaultThreadContextMap
365      * @see org.apache.logging.log4j.spi.CopyOnWriteSortedArrayThreadContextMap
366      * @see org.apache.logging.log4j.spi.GarbageFreeSortedArrayThreadContextMap
367      * @since 2.8
368      */
369     public static ReadOnlyThreadContextMap getThreadContextMap() {
370         return readOnlyContextMap;
371     }
372 
373     /**
374      * Returns true if the Map is empty.
375      *
376      * @return true if the Map is empty, false otherwise.
377      */
378     public static boolean isEmpty() {
379         return contextMap.isEmpty();
380     }
381 
382     /**
383      * Clears the stack for this thread.
384      */
385     public static void clearStack() {
386         contextStack.clear();
387     }
388 
389     /**
390      * Returns a copy of this thread's stack.
391      *
392      * @return A copy of this thread's stack.
393      */
394     public static ContextStack cloneStack() {
395         return contextStack.copy();
396     }
397 
398     /**
399      * Gets an immutable copy of this current thread's context stack.
400      *
401      * @return an immutable copy of the ThreadContext stack.
402      */
403     public static ContextStack getImmutableStack() {
404         final ContextStack result = contextStack.getImmutableStackOrNull();
405         return result == null ? EMPTY_STACK : result;
406     }
407 
408     /**
409      * Sets this thread's stack.
410      *
411      * @param stack The stack to use.
412      */
413     public static void setStack(final Collection<String> stack) {
414         if (stack.isEmpty() || !useStack) {
415             return;
416         }
417         contextStack.clear();
418         contextStack.addAll(stack);
419     }
420 
421     /**
422      * Gets the current nesting depth of this thread's stack.
423      *
424      * @return the number of items in the stack.
425      *
426      * @see #trim
427      */
428     public static int getDepth() {
429         return contextStack.getDepth();
430     }
431 
432     /**
433      * Returns the value of the last item placed on the stack.
434      *
435      * <p>
436      * The returned value is the value that was pushed last. If no context is available, then the empty string "" is
437      * returned.
438      * </p>
439      *
440      * @return String The innermost diagnostic context.
441      */
442     public static String pop() {
443         return contextStack.pop();
444     }
445 
446     /**
447      * Looks at the last diagnostic context at the top of this NDC without removing it.
448      *
449      * <p>
450      * The returned value is the value that was pushed last. If no context is available, then the empty string "" is
451      * returned.
452      * </p>
453      *
454      * @return String The innermost diagnostic context.
455      */
456     public static String peek() {
457         return contextStack.peek();
458     }
459 
460     /**
461      * Pushes new diagnostic context information for the current thread.
462      *
463      * <p>
464      * The contents of the <code>message</code> parameter is determined solely by the client.
465      * </p>
466      *
467      * @param message The new diagnostic context information.
468      */
469     public static void push(final String message) {
470         contextStack.push(message);
471     }
472 
473     /**
474      * Pushes new diagnostic context information for the current thread.
475      *
476      * <p>
477      * The contents of the <code>message</code> and args parameters are determined solely by the client. The message
478      * will be treated as a format String and tokens will be replaced with the String value of the arguments in
479      * accordance with ParameterizedMessage.
480      * </p>
481      *
482      * @param message The new diagnostic context information.
483      * @param args Parameters for the message.
484      */
485     public static void push(final String message, final Object... args) {
486         contextStack.push(ParameterizedMessage.format(message, args));
487     }
488 
489     /**
490      * Removes the diagnostic context for this thread.
491      *
492      * <p>
493      * Each thread that created a diagnostic context by calling {@link #push} should call this method before exiting.
494      * Otherwise, the memory used by the <b>thread</b> cannot be reclaimed by the VM.
495      * </p>
496      *
497      * <p>
498      * As this is such an important problem in heavy duty systems and because it is difficult to always guarantee that
499      * the remove method is called before exiting a thread, this method has been augmented to lazily remove references
500      * to dead threads. In practice, this means that you can be a little sloppy and occasionally forget to call
501      * {@link #remove} before exiting a thread. However, you must call <code>remove</code> sometime. If you never call
502      * it, then your application is sure to run out of memory.
503      * </p>
504      */
505     public static void removeStack() {
506         contextStack.clear();
507     }
508 
509     /**
510      * Trims elements from this diagnostic context. If the current depth is smaller or equal to <code>maxDepth</code>,
511      * then no action is taken. If the current depth is larger than newDepth then all elements at maxDepth or higher are
512      * discarded.
513      *
514      * <p>
515      * This method is a convenient alternative to multiple {@link #pop} calls. Moreover, it is often the case that at
516      * the end of complex call sequences, the depth of the ThreadContext is unpredictable. The <code>trim</code> method
517      * circumvents this problem.
518      * </p>
519      *
520      * <p>
521      * For example, the combination
522      * </p>
523      *
524      * <pre>
525      * void foo() {
526      *     final int depth = ThreadContext.getDepth();
527      *
528      *     // ... complex sequence of calls
529      *
530      *     ThreadContext.trim(depth);
531      * }
532      * </pre>
533      *
534      * <p>
535      * ensures that between the entry and exit of {@code foo} the depth of the diagnostic stack is conserved.
536      * </p>
537      *
538      * @see #getDepth
539      * @param depth The number of elements to keep.
540      */
541     public static void trim(final int depth) {
542         contextStack.trim(depth);
543     }
544 
545     /**
546      * The ThreadContext Stack interface.
547      */
548     public interface ContextStack extends Serializable, Collection<String> {
549 
550         /**
551          * Returns the element at the top of the stack.
552          *
553          * @return The element at the top of the stack.
554          * @throws java.util.NoSuchElementException if the stack is empty.
555          */
556         String pop();
557 
558         /**
559          * Returns the element at the top of the stack without removing it or null if the stack is empty.
560          *
561          * @return the element at the top of the stack or null if the stack is empty.
562          */
563         String peek();
564 
565         /**
566          * Pushes an element onto the stack.
567          *
568          * @param message The element to add.
569          */
570         void push(String message);
571 
572         /**
573          * Returns the number of elements in the stack.
574          *
575          * @return the number of elements in the stack.
576          */
577         int getDepth();
578 
579         /**
580          * Returns all the elements in the stack in a List.
581          *
582          * @return all the elements in the stack in a List.
583          */
584         List<String> asList();
585 
586         /**
587          * Trims elements from the end of the stack.
588          *
589          * @param depth The maximum number of items in the stack to keep.
590          */
591         void trim(int depth);
592 
593         /**
594          * Returns a copy of the ContextStack.
595          *
596          * @return a copy of the ContextStack.
597          */
598         ContextStack copy();
599 
600         /**
601          * Returns a ContextStack with the same contents as this ContextStack or {@code null}. Attempts to modify the
602          * returned stack may or may not throw an exception, but will not affect the contents of this ContextStack.
603          *
604          * @return a ContextStack with the same contents as this ContextStack or {@code null}.
605          */
606         ContextStack getImmutableStackOrNull();
607     }
608 }