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