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