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