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.NoOpThreadContextMap;
33 import org.apache.logging.log4j.spi.ReadOnlyThreadContextMap;
34 import org.apache.logging.log4j.spi.ThreadContextMap;
35 import org.apache.logging.log4j.spi.ThreadContextMap2;
36 import org.apache.logging.log4j.spi.CleanableThreadContextMap;
37 import org.apache.logging.log4j.spi.ThreadContextMapFactory;
38 import org.apache.logging.log4j.spi.ThreadContextStack;
39 import org.apache.logging.log4j.util.PropertiesUtil;
40
41 /**
42 * The ThreadContext allows applications to store information either in a Map or a Stack.
43 * <p>
44 * <b><em>The MDC is managed on a per thread basis</em></b>. To enable automatic inheritance of <i>copies</i> of the MDC
45 * to newly created threads, enable the {@value DefaultThreadContextMap#INHERITABLE_MAP} Log4j system property.
46 * </p>
47 * @see <a href="https://logging.apache.org/log4j/2.x/manual/thread-context.html">Thread Context Manual</a>
48 */
49 public final class ThreadContext {
50
51 /**
52 * An empty read-only ThreadContextStack.
53 */
54 private static class EmptyThreadContextStack extends AbstractCollection<String> implements ThreadContextStack {
55
56 private static final long serialVersionUID = 1L;
57
58 private static final Iterator<String> EMPTY_ITERATOR = new EmptyIterator<>();
59
60 @Override
61 public String pop() {
62 return null;
63 }
64
65 @Override
66 public String peek() {
67 return null;
68 }
69
70 @Override
71 public void push(final String message) {
72 throw new UnsupportedOperationException();
73 }
74
75 @Override
76 public int getDepth() {
77 return 0;
78 }
79
80 @Override
81 public List<String> asList() {
82 return Collections.emptyList();
83 }
84
85 @Override
86 public void trim(final int depth) {
87 // Do nothing
88 }
89
90 @Override
91 public boolean equals(final Object o) {
92 // Similar to java.util.Collections.EmptyList.equals(Object)
93 return (o instanceof Collection) && ((Collection<?>) o).isEmpty();
94 }
95
96 @Override
97 public int hashCode() {
98 // Same as java.util.Collections.EmptyList.hashCode()
99 return 1;
100 }
101
102 @Override
103 public ContextStack copy() {
104 return this;
105 }
106
107 @Override
108 public <T> T[] toArray(final T[] a) {
109 throw new UnsupportedOperationException();
110 }
111
112 @Override
113 public boolean add(final String e) {
114 throw new UnsupportedOperationException();
115 }
116
117 @Override
118 public boolean containsAll(final Collection<?> c) {
119 return false;
120 }
121
122 @Override
123 public boolean addAll(final Collection<? extends String> c) {
124 throw new UnsupportedOperationException();
125 }
126
127 @Override
128 public boolean removeAll(final Collection<?> c) {
129 throw new UnsupportedOperationException();
130 }
131
132 @Override
133 public boolean retainAll(final Collection<?> c) {
134 throw new UnsupportedOperationException();
135 }
136
137 @Override
138 public Iterator<String> iterator() {
139 return EMPTY_ITERATOR;
140 }
141
142 @Override
143 public int size() {
144 return 0;
145 }
146
147 @Override
148 public ContextStack getImmutableStackOrNull() {
149 return this;
150 }
151 }
152
153 /**
154 * An empty iterator. Since Java 1.7 added the Collections.emptyIterator() method, we have to make do.
155 *
156 * @param <E> the type of the empty iterator
157 */
158 private static class EmptyIterator<E> implements Iterator<E> {
159
160 @Override
161 public boolean hasNext() {
162 return false;
163 }
164
165 @Override
166 public E next() {
167 throw new NoSuchElementException("This is an empty iterator!");
168 }
169
170 @Override
171 public void remove() {
172 // no-op
173 }
174 }
175
176 /**
177 * Empty, immutable Map.
178 */
179 // ironically, this annotation gives an "unsupported @SuppressWarnings" warning in Eclipse
180 @SuppressWarnings("PublicStaticCollectionField")
181 // I like irony, so I won't delete it...
182 public static final Map<String, String> EMPTY_MAP = Collections.emptyMap();
183
184 /**
185 * Empty, immutable ContextStack.
186 */
187 // ironically, this annotation gives an "unsupported @SuppressWarnings" warning in Eclipse
188 @SuppressWarnings("PublicStaticCollectionField")
189 public static final ThreadContextStack EMPTY_STACK = new EmptyThreadContextStack();
190
191 private static final String DISABLE_MAP = "disableThreadContextMap";
192 private static final String DISABLE_STACK = "disableThreadContextStack";
193 private static final String DISABLE_ALL = "disableThreadContext";
194
195 private static boolean disableAll;
196 private static boolean useMap;
197 private static boolean useStack;
198 private static ThreadContextMap contextMap;
199 private static ThreadContextStack contextStack;
200 private static ReadOnlyThreadContextMap readOnlyContextMap;
201
202 static {
203 init();
204 }
205
206 private ThreadContext() {
207 // empty
208 }
209
210 /**
211 * <em>Consider private, used for testing.</em>
212 */
213 static void init() {
214 contextMap = null;
215 final PropertiesUtil managerProps = PropertiesUtil.getProperties();
216 disableAll = managerProps.getBooleanProperty(DISABLE_ALL);
217 useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || disableAll);
218 useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || disableAll);
219
220 contextStack = new DefaultThreadContextStack(useStack);
221 if (!useMap) {
222 contextMap = new NoOpThreadContextMap();
223 } else {
224 contextMap = ThreadContextMapFactory.createThreadContextMap();
225 }
226 if (contextMap instanceof ReadOnlyThreadContextMap) {
227 readOnlyContextMap = (ReadOnlyThreadContextMap) contextMap;
228 }
229 }
230
231 /**
232 * Puts a context value (the <code>value</code> parameter) as identified with the <code>key</code> parameter into
233 * the current thread's context map.
234 *
235 * <p>
236 * If the current thread does not have a context map it is created as a side effect.
237 * </p>
238 *
239 * @param key The key name.
240 * @param value The key value.
241 */
242 public static void put(final String key, final String value) {
243 contextMap.put(key, value);
244 }
245
246 /**
247 * Puts all given context map entries into the current thread's
248 * context map.
249 *
250 * <p>If the current thread does not have a context map it is
251 * created as a side effect.</p>
252 * @param m The map.
253 * @since 2.7
254 */
255 public static void putAll(final Map<String, String> m) {
256 if (contextMap instanceof ThreadContextMap2) {
257 ((ThreadContextMap2) contextMap).putAll(m);
258 } else if (contextMap instanceof DefaultThreadContextMap) {
259 ((DefaultThreadContextMap) contextMap).putAll(m);
260 } else {
261 for (final Map.Entry<String, String> entry: m.entrySet()) {
262 contextMap.put(entry.getKey(), entry.getValue());
263 }
264 }
265 }
266
267 /**
268 * Gets the context value identified by the <code>key</code> parameter.
269 *
270 * <p>
271 * This method has no side effects.
272 * </p>
273 *
274 * @param key The key to locate.
275 * @return The value associated with the key or null.
276 */
277 public static String get(final String key) {
278 return contextMap.get(key);
279 }
280
281 /**
282 * Removes the context value identified by the <code>key</code> parameter.
283 *
284 * @param key The key to remove.
285 */
286 public static void remove(final String key) {
287 contextMap.remove(key);
288 }
289
290 /**
291 * Removes the context values identified by the <code>keys</code> parameter.
292 *
293 * @param keys The keys to remove.
294 *
295 * @since 2.8
296 */
297 public static void removeAll(final Iterable<String> keys) {
298 if (contextMap instanceof CleanableThreadContextMap) {
299 ((CleanableThreadContextMap) contextMap).removeAll(keys);
300 } else if (contextMap instanceof DefaultThreadContextMap) {
301 ((DefaultThreadContextMap) contextMap).removeAll(keys);
302 } else {
303 for (final String key : keys) {
304 contextMap.remove(key);
305 }
306 }
307 }
308
309 /**
310 * Clears the context map.
311 */
312 public static void clearMap() {
313 contextMap.clear();
314 }
315
316 /**
317 * Clears the context map and stack.
318 */
319 public static void clearAll() {
320 clearMap();
321 clearStack();
322 }
323
324 /**
325 * Determines if the key is in the context.
326 *
327 * @param key The key to locate.
328 * @return True if the key is in the context, false otherwise.
329 */
330 public static boolean containsKey(final String key) {
331 return contextMap.containsKey(key);
332 }
333
334 /**
335 * Returns a mutable copy of current thread's context Map.
336 *
337 * @return a mutable copy of the context.
338 */
339 public static Map<String, String> getContext() {
340 return contextMap.getCopy();
341 }
342
343 /**
344 * Returns an immutable view of the current thread's context Map.
345 *
346 * @return An immutable view of the ThreadContext Map.
347 */
348 public static Map<String, String> getImmutableContext() {
349 final Map<String, String> map = contextMap.getImmutableMapOrNull();
350 return map == null ? EMPTY_MAP : map;
351 }
352
353 /**
354 * Returns a read-only view of the internal data structure used to store thread context key-value pairs,
355 * or {@code null} if the internal data structure does not implement the
356 * {@code ReadOnlyThreadContextMap} interface.
357 * <p>
358 * The {@link DefaultThreadContextMap} implementation does not implement {@code ReadOnlyThreadContextMap}, so by
359 * default this method returns {@code null}.
360 * </p>
361 *
362 * @return the internal data structure used to store thread context key-value pairs or {@code null}
363 * @see ThreadContextMapFactory
364 * @see DefaultThreadContextMap
365 * @see org.apache.logging.log4j.spi.CopyOnWriteSortedArrayThreadContextMap
366 * @see org.apache.logging.log4j.spi.GarbageFreeSortedArrayThreadContextMap
367 * @since 2.8
368 */
369 public static ReadOnlyThreadContextMap getThreadContextMap() {
370 return readOnlyContextMap;
371 }
372
373 /**
374 * Returns true if the Map is empty.
375 *
376 * @return true if the Map is empty, false otherwise.
377 */
378 public static boolean isEmpty() {
379 return contextMap.isEmpty();
380 }
381
382 /**
383 * Clears the stack for this thread.
384 */
385 public static void clearStack() {
386 contextStack.clear();
387 }
388
389 /**
390 * Returns a copy of this thread's stack.
391 *
392 * @return A copy of this thread's stack.
393 */
394 public static ContextStack cloneStack() {
395 return contextStack.copy();
396 }
397
398 /**
399 * Gets an immutable copy of this current thread's context stack.
400 *
401 * @return an immutable copy of the ThreadContext stack.
402 */
403 public static ContextStack getImmutableStack() {
404 final ContextStack result = contextStack.getImmutableStackOrNull();
405 return result == null ? EMPTY_STACK : result;
406 }
407
408 /**
409 * Sets this thread's stack.
410 *
411 * @param stack The stack to use.
412 */
413 public static void setStack(final Collection<String> stack) {
414 if (stack.isEmpty() || !useStack) {
415 return;
416 }
417 contextStack.clear();
418 contextStack.addAll(stack);
419 }
420
421 /**
422 * Gets the current nesting depth of this thread's stack.
423 *
424 * @return the number of items in the stack.
425 *
426 * @see #trim
427 */
428 public static int getDepth() {
429 return contextStack.getDepth();
430 }
431
432 /**
433 * Returns the value of the last item placed on the stack.
434 *
435 * <p>
436 * The returned value is the value that was pushed last. If no context is available, then the empty string "" is
437 * returned.
438 * </p>
439 *
440 * @return String The innermost diagnostic context.
441 */
442 public static String pop() {
443 return contextStack.pop();
444 }
445
446 /**
447 * Looks at the last diagnostic context at the top of this NDC without removing it.
448 *
449 * <p>
450 * The returned value is the value that was pushed last. If no context is available, then the empty string "" is
451 * returned.
452 * </p>
453 *
454 * @return String The innermost diagnostic context.
455 */
456 public static String peek() {
457 return contextStack.peek();
458 }
459
460 /**
461 * Pushes new diagnostic context information for the current thread.
462 *
463 * <p>
464 * The contents of the <code>message</code> parameter is determined solely by the client.
465 * </p>
466 *
467 * @param message The new diagnostic context information.
468 */
469 public static void push(final String message) {
470 contextStack.push(message);
471 }
472
473 /**
474 * Pushes new diagnostic context information for the current thread.
475 *
476 * <p>
477 * The contents of the <code>message</code> and args parameters are determined solely by the client. The message
478 * will be treated as a format String and tokens will be replaced with the String value of the arguments in
479 * accordance with ParameterizedMessage.
480 * </p>
481 *
482 * @param message The new diagnostic context information.
483 * @param args Parameters for the message.
484 */
485 public static void push(final String message, final Object... args) {
486 contextStack.push(ParameterizedMessage.format(message, args));
487 }
488
489 /**
490 * Removes the diagnostic context for this thread.
491 *
492 * <p>
493 * Each thread that created a diagnostic context by calling {@link #push} should call this method before exiting.
494 * Otherwise, the memory used by the <b>thread</b> cannot be reclaimed by the VM.
495 * </p>
496 *
497 * <p>
498 * As this is such an important problem in heavy duty systems and because it is difficult to always guarantee that
499 * the remove method is called before exiting a thread, this method has been augmented to lazily remove references
500 * to dead threads. In practice, this means that you can be a little sloppy and occasionally forget to call
501 * {@link #remove} before exiting a thread. However, you must call <code>remove</code> sometime. If you never call
502 * it, then your application is sure to run out of memory.
503 * </p>
504 */
505 public static void removeStack() {
506 contextStack.clear();
507 }
508
509 /**
510 * Trims elements from this diagnostic context. If the current depth is smaller or equal to <code>maxDepth</code>,
511 * then no action is taken. If the current depth is larger than newDepth then all elements at maxDepth or higher are
512 * discarded.
513 *
514 * <p>
515 * This method is a convenient alternative to multiple {@link #pop} calls. Moreover, it is often the case that at
516 * the end of complex call sequences, the depth of the ThreadContext is unpredictable. The <code>trim</code> method
517 * circumvents this problem.
518 * </p>
519 *
520 * <p>
521 * For example, the combination
522 * </p>
523 *
524 * <pre>
525 * void foo() {
526 * final int depth = ThreadContext.getDepth();
527 *
528 * // ... complex sequence of calls
529 *
530 * ThreadContext.trim(depth);
531 * }
532 * </pre>
533 *
534 * <p>
535 * ensures that between the entry and exit of {@code foo} the depth of the diagnostic stack is conserved.
536 * </p>
537 *
538 * @see #getDepth
539 * @param depth The number of elements to keep.
540 */
541 public static void trim(final int depth) {
542 contextStack.trim(depth);
543 }
544
545 /**
546 * The ThreadContext Stack interface.
547 */
548 public interface ContextStack extends Serializable, Collection<String> {
549
550 /**
551 * Returns the element at the top of the stack.
552 *
553 * @return The element at the top of the stack.
554 * @throws java.util.NoSuchElementException if the stack is empty.
555 */
556 String pop();
557
558 /**
559 * Returns the element at the top of the stack without removing it or null if the stack is empty.
560 *
561 * @return the element at the top of the stack or null if the stack is empty.
562 */
563 String peek();
564
565 /**
566 * Pushes an element onto the stack.
567 *
568 * @param message The element to add.
569 */
570 void push(String message);
571
572 /**
573 * Returns the number of elements in the stack.
574 *
575 * @return the number of elements in the stack.
576 */
577 int getDepth();
578
579 /**
580 * Returns all the elements in the stack in a List.
581 *
582 * @return all the elements in the stack in a List.
583 */
584 List<String> asList();
585
586 /**
587 * Trims elements from the end of the stack.
588 *
589 * @param depth The maximum number of items in the stack to keep.
590 */
591 void trim(int depth);
592
593 /**
594 * Returns a copy of the ContextStack.
595 *
596 * @return a copy of the ContextStack.
597 */
598 ContextStack copy();
599
600 /**
601 * Returns a ContextStack with the same contents as this ContextStack or {@code null}. Attempts to modify the
602 * returned stack may or may not throw an exception, but will not affect the contents of this ContextStack.
603 *
604 * @return a ContextStack with the same contents as this ContextStack or {@code null}.
605 */
606 ContextStack getImmutableStackOrNull();
607 }
608 }