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 final 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 *
259 * <p>If the current thread does not have a context map it is
260 * created as a side effect.</p>
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 *
271 * <p>This method has no side effects.</p>
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 final 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 *
385 * <p>The returned value is the value that was pushed last. If no
386 * context is available, then the empty string "" is returned.</p>
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 *
398 * <p>The returned value is the value that was pushed last. If no
399 * context is available, then the empty string "" is returned.</p>
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 *
410 * <p>The contents of the <code>message</code> parameter is
411 * determined solely by the client.</p>
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 *
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.</p>
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 *
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.</p>
440 *
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.</p>
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 *
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.</p>
466 *
467 * <p>For example, the combination</p>
468 * <pre>
469 * void foo() {
470 * final int depth = ThreadContext.getDepth();
471 *
472 * // ... complex sequence of calls
473 *
474 * ThreadContext.trim(depth);
475 * }
476 * </pre>
477 *
478 * <p>ensures that between the entry and exit of {@code foo} the depth of the
479 * diagnostic stack is conserved.</p>
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 }