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.ArrayList;
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  
28  import org.apache.logging.log4j.message.ParameterizedMessage;
29  import org.apache.logging.log4j.spi.DefaultThreadContextMap;
30  import org.apache.logging.log4j.spi.DefaultThreadContextStack;
31  import org.apache.logging.log4j.spi.LoggerContextFactory;
32  import org.apache.logging.log4j.spi.MutableThreadContextStack;
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       * Empty, immutable Map.
51       */
52      public static final Map<String, String> EMPTY_MAP = Collections.emptyMap();
53  
54      /**
55       * Empty, immutable ContextStack.
56       */
57      public static final ThreadContextStack EMPTY_STACK = new MutableThreadContextStack(new ArrayList<String>());
58  
59      private static final String DISABLE_MAP = "disableThreadContextMap";
60      private static final String DISABLE_STACK = "disableThreadContextStack";
61      private static final String DISABLE_ALL = "disableThreadContext";
62      private static final String THREAD_CONTEXT_KEY = "log4j2.threadContextMap";
63  
64      private static boolean all;
65      private static boolean useMap;
66      private static boolean useStack;
67      private static ThreadContextMap contextMap;
68      private static ThreadContextStack contextStack;
69      private static final Logger LOGGER = StatusLogger.getLogger();
70  
71      static {
72          final PropertiesUtil managerProps = PropertiesUtil.getProperties();
73          all = managerProps.getBooleanProperty(DISABLE_ALL);
74          useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || all);
75          contextStack = new DefaultThreadContextStack(useStack);
76  
77          useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || all);
78          String threadContextMapName = managerProps.getStringProperty(THREAD_CONTEXT_KEY);
79          final ClassLoader cl = ProviderUtil.findClassLoader();
80          if (threadContextMapName != null) {
81              try {
82                  final Class<?> clazz = cl.loadClass(threadContextMapName);
83                  if (ThreadContextMap.class.isAssignableFrom(clazz)) {
84                      contextMap = (ThreadContextMap) clazz.newInstance();
85                  }
86              } catch (final ClassNotFoundException cnfe) {
87                  LOGGER.error("Unable to locate configured LoggerContextFactory {}", threadContextMapName);
88              } catch (final Exception ex) {
89                  LOGGER.error("Unable to create configured LoggerContextFactory {}", threadContextMapName, ex);
90              }
91          }
92          if (contextMap == null && ProviderUtil.hasProviders()) {
93              final LoggerContextFactory factory = LogManager.getFactory();
94              final Iterator<Provider> providers = ProviderUtil.getProviders();
95              while (providers.hasNext()) {
96                  final Provider provider = providers.next();
97                  threadContextMapName = provider.getThreadContextMap();
98                  final String factoryClassName = provider.getClassName();
99                  if (threadContextMapName != null && factory.getClass().getName().equals(factoryClassName)) {
100                     try {
101                         final Class<?> clazz = cl.loadClass(threadContextMapName);
102                         if (ThreadContextMap.class.isAssignableFrom(clazz)) {
103                             contextMap = (ThreadContextMap) clazz.newInstance();
104                             break;
105                         }
106                     } catch (final ClassNotFoundException cnfe) {
107                         LOGGER.error("Unable to locate configured LoggerContextFactory {}", threadContextMapName);
108                         contextMap = new DefaultThreadContextMap(useMap);
109                     } catch (final Exception ex) {
110                         LOGGER.error("Unable to create configured LoggerContextFactory {}", threadContextMapName, ex);
111                         contextMap = new DefaultThreadContextMap(useMap);
112                     }
113                 }
114             }
115         }
116         if (contextMap == null) {
117             contextMap = new DefaultThreadContextMap(useMap);
118         }
119     }
120 
121     private ThreadContext() {
122 
123     }
124 
125     /**
126      * Put a context value (the <code>value</code> parameter) as identified
127      * with the <code>key</code> parameter into the current thread's
128      * context map.
129      * <p/>
130      * <p>If the current thread does not have a context map it is
131      * created as a side effect.
132      * @param key The key name.
133      * @param value The key value.
134      */
135     public static void put(final String key, final String value) {
136         contextMap.put(key, value);
137     }
138 
139     /**
140      * Get the context value identified by the <code>key</code> parameter.
141      * <p/>
142      * <p>This method has no side effects.
143      * @param key The key to locate.
144      * @return The value associated with the key or null.
145      */
146     public static String get(final String key) {
147         return contextMap.get(key);
148     }
149 
150     /**
151      * Remove the context value identified by the <code>key</code> parameter.
152      * @param key The key to remove.
153      */
154     public static void remove(final String key) {
155         contextMap.remove(key);
156     }
157 
158     /**
159      * Clear the context.
160      */
161     public static void clear() {
162         contextMap.clear();
163     }
164 
165     /**
166      * Determine if the key is in the context.
167      * @param key The key to locate.
168      * @return True if the key is in the context, false otherwise.
169      */
170     public static boolean containsKey(final String key) {
171         return contextMap.containsKey(key);
172     }
173 
174     /**
175      * Returns a mutable copy of current thread's context Map.
176      * @return a mutable copy of the context.
177      */
178     public static Map<String, String> getContext() {
179         return contextMap.getCopy();
180     }
181 
182     /**
183      * Returns an immutable view of the current thread's context Map.
184      * @return An immutable view of the ThreadContext Map.
185      */
186     public static Map<String, String> getImmutableContext() {
187         final Map<String, String> map = contextMap.getImmutableMapOrNull();
188         return map == null ? EMPTY_MAP : map;
189     }
190 
191     /**
192      * Returns true if the Map is empty.
193      * @return true if the Map is empty, false otherwise.
194      */
195     public static boolean isEmpty() {
196         return contextMap.isEmpty();
197     }
198 
199     /**
200      * Clear the stack for this thread.
201      */
202     public static void clearStack() {
203         contextStack.clear();
204     }
205 
206     /**
207      * Returns a copy of this thread's stack.
208      * @return A copy of this thread's stack.
209      */
210     public static ContextStack cloneStack() {
211         return contextStack.copy();
212     }
213 
214     /**
215      * Get an immutable copy of this current thread's context stack.
216      * @return an immutable copy of the ThreadContext stack.
217      */
218     public static ContextStack getImmutableStack() {
219         return contextStack;
220     }
221 
222     /**
223      * Set this thread's stack.
224      * @param stack The stack to use.
225      */
226     public static void setStack(final Collection<String> stack) {
227         if (stack.size() == 0 || !useStack) {
228             return;
229         }
230         contextStack.clear();
231         contextStack.addAll(stack);
232     }
233 
234     /**
235      * Get the current nesting depth of this thread's stack.
236      * @return the number of items in the stack.
237      *
238      * @see #trim
239      */
240     public static int getDepth() {
241         return contextStack.getDepth();
242     }
243 
244     /**
245      * Returns the value of the last item placed on the stack.
246      * <p/>
247      * <p>The returned value is the value that was pushed last. If no
248      * context is available, then the empty string "" is returned.
249      *
250      * @return String The innermost diagnostic context.
251      */
252     public static String pop() {
253         return contextStack.pop();
254     }
255 
256     /**
257      * Looks at the last diagnostic context at the top of this NDC
258      * without removing it.
259      * <p/>
260      * <p>The returned value is the value that was pushed last. If no
261      * context is available, then the empty string "" is returned.
262      *
263      * @return String The innermost diagnostic context.
264      */
265     public static String peek() {
266         return contextStack.peek();
267     }
268 
269     /**
270      * Push new diagnostic context information for the current thread.
271      * <p/>
272      * <p>The contents of the <code>message</code> parameter is
273      * determined solely by the client.
274      *
275      * @param message The new diagnostic context information.
276      */
277     public static void push(final String message) {
278         contextStack.push(message);
279     }
280     /**
281      * Push new diagnostic context information for the current thread.
282      * <p/>
283      * <p>The contents of the <code>message</code> and args parameters are
284      * determined solely by the client. The message will be treated as a format String
285      * and tokens will be replaced with the String value of the arguments in accordance
286      * with ParameterizedMessage.
287      *
288      * @param message The new diagnostic context information.
289      * @param args Parameters for the message.
290      */
291     public static void push(final String message, final Object... args) {
292         contextStack.push(ParameterizedMessage.format(message, args));
293     }
294 
295     /**
296      * Remove the diagnostic context for this thread.
297      * <p/>
298      * <p>Each thread that created a diagnostic context by calling
299      * {@link #push} should call this method before exiting. Otherwise,
300      * the memory used by the <b>thread</b> cannot be reclaimed by the
301      * VM.
302      * <p/>
303      * <p>As this is such an important problem in heavy duty systems and
304      * because it is difficult to always guarantee that the remove
305      * method is called before exiting a thread, this method has been
306      * augmented to lazily remove references to dead threads. In
307      * practice, this means that you can be a little sloppy and
308      * occasionally forget to call {@link #remove} before exiting a
309      * thread. However, you must call <code>remove</code> sometime. If
310      * you never call it, then your application is sure to run out of
311      * memory.
312      */
313     public static void removeStack() {
314         contextStack.clear();
315     }
316 
317     /**
318      * Trims elements from this diagnostic context. If the current
319      * depth is smaller or equal to <code>maxDepth</code>, then no
320      * action is taken. If the current depth is larger than newDepth
321      * then all elements at maxDepth or higher are discarded.
322      * <p/>
323      * <p>This method is a convenient alternative to multiple {@link
324      * #pop} calls. Moreover, it is often the case that at the end of
325      * complex call sequences, the depth of the ThreadContext is
326      * unpredictable. The <code>trim</code> method circumvents
327      * this problem.
328      * <p/>
329      * <p>For example, the combination
330      * <pre>
331      * void foo() {
332      * &nbsp;  int depth = ThreadContext.getDepth();
333      * <p/>
334      * &nbsp;  ... complex sequence of calls
335      * <p/>
336      * &nbsp;  ThreadContext.trim(depth);
337      * }
338      * </pre>
339      * <p/>
340      * ensures that between the entry and exit of foo the depth of the
341      * diagnostic stack is conserved.
342      *
343      * @see #getDepth
344      * @param depth The number of elements to keep.
345      */
346     public static void trim(final int depth) {
347         contextStack.trim(depth);
348     }
349 
350     /**
351      * The ThreadContext Stack interface.
352      */
353     public interface ContextStack extends Serializable {
354 
355         /**
356          * Clears all elements from the stack.
357          */
358         void clear();
359 
360         /**
361          * Returns the element at the top of the stack.
362          * @return The element at the top of the stack.
363          * @throws java.util.NoSuchElementException if the stack is empty.
364          */
365         String pop();
366 
367         /**
368          * Returns the element at the top of the stack without removing it or null if the stack is empty.
369          * @return the element at the top of the stack or null if the stack is empty.
370          */
371         String peek();
372 
373         /**
374          * Add an element to the stack.
375          * @param message The element to add.
376          */
377         void push(String message);
378 
379         /**
380          * Returns the number of elements in the stack.
381          * @return the number of elements in the stack.
382          */
383         int getDepth();
384 
385         /**
386          * Returns all the elements in the stack in a List.
387          * @return all the elements in the stack in a List.
388          */
389         List<String> asList();
390 
391         /**
392          * Trims elements from the end of the stack.
393          * @param depth The maximum number of items in the stack to keep.
394          */
395         void trim(int depth);
396 
397         /**
398          * Returns a copy of the ContextStack.
399          * @return a copy of the ContextStack.s
400          */
401         ContextStack copy();
402     }
403 }