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.LoggerContextFactory;
33  import org.apache.logging.log4j.spi.Provider;
34  import org.apache.logging.log4j.spi.ThreadContextMap;
35  import org.apache.logging.log4j.spi.ThreadContextStack;
36  import org.apache.logging.log4j.status.StatusLogger;
37  import org.apache.logging.log4j.util.PropertiesUtil;
38  import org.apache.logging.log4j.util.ProviderUtil;
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<String>();
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      * @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") // I like irony, so I won't delete it...
178     public static final Map<String, String> EMPTY_MAP = Collections.emptyMap();
179 
180     /**
181      * Empty, immutable ContextStack.
182      */
183     // ironically, this annotation gives an "unsupported @SuppressWarnings" warning in Eclipse
184     @SuppressWarnings("PublicStaticCollectionField")
185     public static final ThreadContextStack EMPTY_STACK = new EmptyThreadContextStack();
186 
187     private static final String DISABLE_MAP = "disableThreadContextMap";
188     private static final String DISABLE_STACK = "disableThreadContextStack";
189     private static final String DISABLE_ALL = "disableThreadContext";
190     private static final String THREAD_CONTEXT_KEY = "log4j2.threadContextMap";
191 
192     private static boolean disableAll;
193     private static boolean useMap;
194     private static boolean useStack;
195     private static ThreadContextMap contextMap;
196     private static ThreadContextStack contextStack;
197     private static final Logger LOGGER = StatusLogger.getLogger();
198 
199     static {
200         init();
201     }
202         
203     /**
204      * <em>Consider private, used for testing.</em>
205      */
206     static void init() {
207         contextMap = null;
208         final PropertiesUtil managerProps = PropertiesUtil.getProperties();
209         disableAll = managerProps.getBooleanProperty(DISABLE_ALL);
210         useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || disableAll);
211         useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || disableAll);
212 
213         contextStack = new DefaultThreadContextStack(useStack);
214         String threadContextMapName = managerProps.getStringProperty(THREAD_CONTEXT_KEY);
215         final ClassLoader cl = ProviderUtil.findClassLoader();
216         if (threadContextMapName != null) {
217             try {
218                 final Class<?> clazz = cl.loadClass(threadContextMapName);
219                 if (ThreadContextMap.class.isAssignableFrom(clazz)) {
220                     contextMap = (ThreadContextMap) clazz.newInstance();
221                 }
222             } catch (final ClassNotFoundException cnfe) {
223                 LOGGER.error("Unable to locate configured LoggerContextFactory {}", threadContextMapName);
224             } catch (final Exception ex) {
225                 LOGGER.error("Unable to create configured LoggerContextFactory {}", threadContextMapName, ex);
226             }
227         }
228         if (contextMap == null && ProviderUtil.hasProviders()) {
229             final LoggerContextFactory factory = LogManager.getFactory();
230             for (final Provider provider : ProviderUtil.getProviders()) {
231                 threadContextMapName = provider.getThreadContextMap();
232                 final String factoryClassName = provider.getClassName();
233                 if (threadContextMapName != null && factory.getClass().getName().equals(factoryClassName)) {
234                     try {
235                         final Class<?> clazz = cl.loadClass(threadContextMapName);
236                         if (ThreadContextMap.class.isAssignableFrom(clazz)) {
237                             contextMap = (ThreadContextMap) clazz.newInstance();
238                             break;
239                         }
240                     } catch (final ClassNotFoundException cnfe) {
241                         LOGGER.error("Unable to locate configured LoggerContextFactory {}", threadContextMapName);
242                         contextMap = new DefaultThreadContextMap(useMap);
243                     } catch (final Exception ex) {
244                         LOGGER.error("Unable to create configured LoggerContextFactory {}", threadContextMapName, ex);
245                         contextMap = new DefaultThreadContextMap(useMap);
246                     }
247                 }
248             }
249         }
250         if (contextMap == null) {
251             contextMap = new DefaultThreadContextMap(useMap);
252         }
253     }
254 
255     private ThreadContext() {
256         // empty
257     }
258 
259     /**
260      * Puts a context value (the <code>value</code> parameter) as identified
261      * with the <code>key</code> parameter into the current thread's
262      * context map.
263      * <p/>
264      * <p>If the current thread does not have a context map it is
265      * created as a side effect.
266      * @param key The key name.
267      * @param value The key value.
268      */
269     public static void put(final String key, final String value) {
270         contextMap.put(key, value);
271     }
272 
273     /**
274      * Gets the context value identified by the <code>key</code> parameter.
275      * <p/>
276      * <p>This method has no side effects.
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      * @param key The key to remove.
287      */
288     public static void remove(final String key) {
289         contextMap.remove(key);
290     }
291 
292     /**
293      * Clears the context map.
294      */
295     public static void clearMap() {
296         contextMap.clear();
297     }
298 
299     /**
300      * Clears the context map and stack.
301      */
302     public static void clearAll() {
303         clearMap();
304         clearStack();
305     }
306 
307     /**
308      * Determines if the key is in the context.
309      * @param key The key to locate.
310      * @return True if the key is in the context, false otherwise.
311      */
312     public static boolean containsKey(final String key) {
313         return contextMap.containsKey(key);
314     }
315 
316     /**
317      * Returns a mutable copy of current thread's context Map.
318      * @return a mutable copy of the context.
319      */
320     public static Map<String, String> getContext() {
321         return contextMap.getCopy();
322     }
323 
324     /**
325      * Returns an immutable view of the current thread's context Map.
326      * @return An immutable view of the ThreadContext Map.
327      */
328     public static Map<String, String> getImmutableContext() {
329         final Map<String, String> map = contextMap.getImmutableMapOrNull();
330         return map == null ? EMPTY_MAP : map;
331     }
332 
333     /**
334      * Returns true if the Map is empty.
335      * @return true if the Map is empty, false otherwise.
336      */
337     public static boolean isEmpty() {
338         return contextMap.isEmpty();
339     }
340 
341     /**
342      * Clears the stack for this thread.
343      */
344     public static void clearStack() {
345         contextStack.clear();
346     }
347 
348     /**
349      * Returns a copy of this thread's stack.
350      * @return A copy of this thread's stack.
351      */
352     public static ContextStack cloneStack() {
353         return contextStack.copy();
354     }
355 
356     /**
357      * Gets an immutable copy of this current thread's context stack.
358      * @return an immutable copy of the ThreadContext stack.
359      */
360     public static ContextStack getImmutableStack() {
361         ContextStack result = contextStack.getImmutableStackOrNull();
362         return result == null ? EMPTY_STACK : result;
363     }
364 
365     /**
366      * Sets this thread's stack.
367      * @param stack The stack to use.
368      */
369     public static void setStack(final Collection<String> stack) {
370         if (stack.isEmpty() || !useStack) {
371             return;
372         }
373         contextStack.clear();
374         contextStack.addAll(stack);
375     }
376 
377     /**
378      * Gets the current nesting depth of this thread's stack.
379      * @return the number of items in the stack.
380      *
381      * @see #trim
382      */
383     public static int getDepth() {
384         return contextStack.getDepth();
385     }
386 
387     /**
388      * Returns the value of the last item placed on the stack.
389      * <p/>
390      * <p>The returned value is the value that was pushed last. If no
391      * context is available, then the empty string "" is returned.
392      *
393      * @return String The innermost diagnostic context.
394      */
395     public static String pop() {
396         return contextStack.pop();
397     }
398 
399     /**
400      * Looks at the last diagnostic context at the top of this NDC
401      * without removing it.
402      * <p/>
403      * <p>The returned value is the value that was pushed last. If no
404      * context is available, then the empty string "" is returned.
405      *
406      * @return String The innermost diagnostic context.
407      */
408     public static String peek() {
409         return contextStack.peek();
410     }
411 
412     /**
413      * Pushes new diagnostic context information for the current thread.
414      * <p/>
415      * <p>The contents of the <code>message</code> parameter is
416      * determined solely by the client.
417      *
418      * @param message The new diagnostic context information.
419      */
420     public static void push(final String message) {
421         contextStack.push(message);
422     }
423     /**
424      * Pushes new diagnostic context information for the current thread.
425      * <p/>
426      * <p>The contents of the <code>message</code> and args parameters are
427      * determined solely by the client. The message will be treated as a format String
428      * and tokens will be replaced with the String value of the arguments in accordance
429      * with ParameterizedMessage.
430      *
431      * @param message The new diagnostic context information.
432      * @param args Parameters for the message.
433      */
434     public static void push(final String message, final Object... args) {
435         contextStack.push(ParameterizedMessage.format(message, args));
436     }
437 
438     /**
439      * Removes the diagnostic context for this thread.
440      * <p/>
441      * <p>Each thread that created a diagnostic context by calling
442      * {@link #push} should call this method before exiting. Otherwise,
443      * the memory used by the <b>thread</b> cannot be reclaimed by the
444      * VM.
445      * <p/>
446      * <p>As this is such an important problem in heavy duty systems and
447      * because it is difficult to always guarantee that the remove
448      * method is called before exiting a thread, this method has been
449      * augmented to lazily remove references to dead threads. In
450      * practice, this means that you can be a little sloppy and
451      * occasionally forget to call {@link #remove} before exiting a
452      * thread. However, you must call <code>remove</code> sometime. If
453      * you never call it, then your application is sure to run out of
454      * memory.
455      */
456     public static void removeStack() {
457         contextStack.clear();
458     }
459 
460     /**
461      * Trims elements from this diagnostic context. If the current
462      * depth is smaller or equal to <code>maxDepth</code>, then no
463      * action is taken. If the current depth is larger than newDepth
464      * then all elements at maxDepth or higher are discarded.
465      * <p/>
466      * <p>This method is a convenient alternative to multiple {@link
467      * #pop} calls. Moreover, it is often the case that at the end of
468      * complex call sequences, the depth of the ThreadContext is
469      * unpredictable. The <code>trim</code> method circumvents
470      * this problem.
471      * <p/>
472      * <p>For example, the combination
473      * <pre>
474      * void foo() {
475      * &nbsp;  int depth = ThreadContext.getDepth();
476      * <p/>
477      * &nbsp;  ... complex sequence of calls
478      * <p/>
479      * &nbsp;  ThreadContext.trim(depth);
480      * }
481      * </pre>
482      * <p/>
483      * ensures that between the entry and exit of foo the depth of the
484      * diagnostic stack is conserved.
485      *
486      * @see #getDepth
487      * @param depth The number of elements to keep.
488      */
489     public static void trim(final int depth) {
490         contextStack.trim(depth);
491     }
492 
493     /**
494      * The ThreadContext Stack interface.
495      */
496     public interface ContextStack extends Serializable, Collection<String> {
497 
498         /**
499          * Returns the element at the top of the stack.
500          * @return The element at the top of the stack.
501          * @throws java.util.NoSuchElementException if the stack is empty.
502          */
503         String pop();
504 
505         /**
506          * Returns the element at the top of the stack without removing it or null if the stack is empty.
507          * @return the element at the top of the stack or null if the stack is empty.
508          */
509         String peek();
510 
511         /**
512          * Pushes an element onto the stack.
513          * @param message The element to add.
514          */
515         void push(String message);
516 
517         /**
518          * Returns the number of elements in the stack.
519          * @return the number of elements in the stack.
520          */
521         int getDepth();
522 
523         /**
524          * Returns all the elements in the stack in a List.
525          * @return all the elements in the stack in a List.
526          */
527         List<String> asList();
528 
529         /**
530          * Trims elements from the end of the stack.
531          * @param depth The maximum number of items in the stack to keep.
532          */
533         void trim(int depth);
534 
535         /**
536          * Returns a copy of the ContextStack.
537          * @return a copy of the ContextStack.
538          */
539         ContextStack copy();
540 
541         /**
542          * Returns a ContextStack with the same contents as this ContextStack or {@code null}.
543          * Attempts to modify the returned stack may or may not throw an exception, but will not affect the contents
544          * of this ContextStack.
545          * @return a ContextStack with the same contents as this ContextStack or {@code null}.
546          */
547         ContextStack getImmutableStackOrNull();
548     }
549 }