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 ThreadContextMapFactory.init();
215 contextMap = null;
216 final PropertiesUtil managerProps = PropertiesUtil.getProperties();
217 disableAll = managerProps.getBooleanProperty(DISABLE_ALL);
218 useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || disableAll);
219 useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || disableAll);
220
221 contextStack = new DefaultThreadContextStack(useStack);
222 if (!useMap) {
223 contextMap = new NoOpThreadContextMap();
224 } else {
225 contextMap = ThreadContextMapFactory.createThreadContextMap();
226 }
227 if (contextMap instanceof ReadOnlyThreadContextMap) {
228 readOnlyContextMap = (ReadOnlyThreadContextMap) contextMap;
229 } else {
230 readOnlyContextMap = null;
231 }
232 }
233
234 /**
235 * Puts a context value (the <code>value</code> parameter) as identified with the <code>key</code> parameter into
236 * the current thread's context map.
237 *
238 * <p>
239 * If the current thread does not have a context map it is created as a side effect.
240 * </p>
241 *
242 * @param key The key name.
243 * @param value The key value.
244 */
245 public static void put(final String key, final String value) {
246 contextMap.put(key, value);
247 }
248
249 /**
250 * Puts all given context map entries into the current thread's
251 * context map.
252 *
253 * <p>If the current thread does not have a context map it is
254 * created as a side effect.</p>
255 * @param m The map.
256 * @since 2.7
257 */
258 public static void putAll(final Map<String, String> m) {
259 if (contextMap instanceof ThreadContextMap2) {
260 ((ThreadContextMap2) contextMap).putAll(m);
261 } else if (contextMap instanceof DefaultThreadContextMap) {
262 ((DefaultThreadContextMap) contextMap).putAll(m);
263 } else {
264 for (final Map.Entry<String, String> entry: m.entrySet()) {
265 contextMap.put(entry.getKey(), entry.getValue());
266 }
267 }
268 }
269
270 /**
271 * Gets the context value identified by the <code>key</code> parameter.
272 *
273 * <p>
274 * This method has no side effects.
275 * </p>
276 *
277 * @param key The key to locate.
278 * @return The value associated with the key or null.
279 */
280 public static String get(final String key) {
281 return contextMap.get(key);
282 }
283
284 /**
285 * Removes the context value identified by the <code>key</code> parameter.
286 *
287 * @param key The key to remove.
288 */
289 public static void remove(final String key) {
290 contextMap.remove(key);
291 }
292
293 /**
294 * Removes the context values identified by the <code>keys</code> parameter.
295 *
296 * @param keys The keys to remove.
297 *
298 * @since 2.8
299 */
300 public static void removeAll(final Iterable<String> keys) {
301 if (contextMap instanceof CleanableThreadContextMap) {
302 ((CleanableThreadContextMap) contextMap).removeAll(keys);
303 } else if (contextMap instanceof DefaultThreadContextMap) {
304 ((DefaultThreadContextMap) contextMap).removeAll(keys);
305 } else {
306 for (final String key : keys) {
307 contextMap.remove(key);
308 }
309 }
310 }
311
312 /**
313 * Clears the context map.
314 */
315 public static void clearMap() {
316 contextMap.clear();
317 }
318
319 /**
320 * Clears the context map and stack.
321 */
322 public static void clearAll() {
323 clearMap();
324 clearStack();
325 }
326
327 /**
328 * Determines if the key is in the context.
329 *
330 * @param key The key to locate.
331 * @return True if the key is in the context, false otherwise.
332 */
333 public static boolean containsKey(final String key) {
334 return contextMap.containsKey(key);
335 }
336
337 /**
338 * Returns a mutable copy of current thread's context Map.
339 *
340 * @return a mutable copy of the context.
341 */
342 public static Map<String, String> getContext() {
343 return contextMap.getCopy();
344 }
345
346 /**
347 * Returns an immutable view of the current thread's context Map.
348 *
349 * @return An immutable view of the ThreadContext Map.
350 */
351 public static Map<String, String> getImmutableContext() {
352 final Map<String, String> map = contextMap.getImmutableMapOrNull();
353 return map == null ? EMPTY_MAP : map;
354 }
355
356 /**
357 * Returns a read-only view of the internal data structure used to store thread context key-value pairs,
358 * or {@code null} if the internal data structure does not implement the
359 * {@code ReadOnlyThreadContextMap} interface.
360 * <p>
361 * The {@link DefaultThreadContextMap} implementation does not implement {@code ReadOnlyThreadContextMap}, so by
362 * default this method returns {@code null}.
363 * </p>
364 *
365 * @return the internal data structure used to store thread context key-value pairs or {@code null}
366 * @see ThreadContextMapFactory
367 * @see DefaultThreadContextMap
368 * @see org.apache.logging.log4j.spi.CopyOnWriteSortedArrayThreadContextMap
369 * @see org.apache.logging.log4j.spi.GarbageFreeSortedArrayThreadContextMap
370 * @since 2.8
371 */
372 public static ReadOnlyThreadContextMap getThreadContextMap() {
373 return readOnlyContextMap;
374 }
375
376 /**
377 * Returns true if the Map is empty.
378 *
379 * @return true if the Map is empty, false otherwise.
380 */
381 public static boolean isEmpty() {
382 return contextMap.isEmpty();
383 }
384
385 /**
386 * Clears the stack for this thread.
387 */
388 public static void clearStack() {
389 contextStack.clear();
390 }
391
392 /**
393 * Returns a copy of this thread's stack.
394 *
395 * @return A copy of this thread's stack.
396 */
397 public static ContextStack cloneStack() {
398 return contextStack.copy();
399 }
400
401 /**
402 * Gets an immutable copy of this current thread's context stack.
403 *
404 * @return an immutable copy of the ThreadContext stack.
405 */
406 public static ContextStack getImmutableStack() {
407 final ContextStack result = contextStack.getImmutableStackOrNull();
408 return result == null ? EMPTY_STACK : result;
409 }
410
411 /**
412 * Sets this thread's stack.
413 *
414 * @param stack The stack to use.
415 */
416 public static void setStack(final Collection<String> stack) {
417 if (stack.isEmpty() || !useStack) {
418 return;
419 }
420 contextStack.clear();
421 contextStack.addAll(stack);
422 }
423
424 /**
425 * Gets the current nesting depth of this thread's stack.
426 *
427 * @return the number of items in the stack.
428 *
429 * @see #trim
430 */
431 public static int getDepth() {
432 return contextStack.getDepth();
433 }
434
435 /**
436 * Returns the value of the last item placed on the stack.
437 *
438 * <p>
439 * The returned value is the value that was pushed last. If no context is available, then the empty string "" is
440 * returned.
441 * </p>
442 *
443 * @return String The innermost diagnostic context.
444 */
445 public static String pop() {
446 return contextStack.pop();
447 }
448
449 /**
450 * Looks at the last diagnostic context at the top of this NDC without removing it.
451 *
452 * <p>
453 * The returned value is the value that was pushed last. If no context is available, then the empty string "" is
454 * returned.
455 * </p>
456 *
457 * @return String The innermost diagnostic context.
458 */
459 public static String peek() {
460 return contextStack.peek();
461 }
462
463 /**
464 * Pushes new diagnostic context information for the current thread.
465 *
466 * <p>
467 * The contents of the <code>message</code> parameter is determined solely by the client.
468 * </p>
469 *
470 * @param message The new diagnostic context information.
471 */
472 public static void push(final String message) {
473 contextStack.push(message);
474 }
475
476 /**
477 * Pushes new diagnostic context information for the current thread.
478 *
479 * <p>
480 * The contents of the <code>message</code> and args parameters are determined solely by the client. The message
481 * will be treated as a format String and tokens will be replaced with the String value of the arguments in
482 * accordance with ParameterizedMessage.
483 * </p>
484 *
485 * @param message The new diagnostic context information.
486 * @param args Parameters for the message.
487 */
488 public static void push(final String message, final Object... args) {
489 contextStack.push(ParameterizedMessage.format(message, args));
490 }
491
492 /**
493 * Removes the diagnostic context for this thread.
494 *
495 * <p>
496 * Each thread that created a diagnostic context by calling {@link #push} should call this method before exiting.
497 * Otherwise, the memory used by the <b>thread</b> cannot be reclaimed by the VM.
498 * </p>
499 *
500 * <p>
501 * As this is such an important problem in heavy duty systems and because it is difficult to always guarantee that
502 * the remove method is called before exiting a thread, this method has been augmented to lazily remove references
503 * to dead threads. In practice, this means that you can be a little sloppy and occasionally forget to call
504 * {@link #remove} before exiting a thread. However, you must call <code>remove</code> sometime. If you never call
505 * it, then your application is sure to run out of memory.
506 * </p>
507 */
508 public static void removeStack() {
509 contextStack.clear();
510 }
511
512 /**
513 * Trims elements from this diagnostic context. If the current depth is smaller or equal to <code>maxDepth</code>,
514 * then no action is taken. If the current depth is larger than newDepth then all elements at maxDepth or higher are
515 * discarded.
516 *
517 * <p>
518 * This method is a convenient alternative to multiple {@link #pop} calls. Moreover, it is often the case that at
519 * the end of complex call sequences, the depth of the ThreadContext is unpredictable. The <code>trim</code> method
520 * circumvents this problem.
521 * </p>
522 *
523 * <p>
524 * For example, the combination
525 * </p>
526 *
527 * <pre>
528 * void foo() {
529 * final int depth = ThreadContext.getDepth();
530 *
531 * // ... complex sequence of calls
532 *
533 * ThreadContext.trim(depth);
534 * }
535 * </pre>
536 *
537 * <p>
538 * ensures that between the entry and exit of {@code foo} the depth of the diagnostic stack is conserved.
539 * </p>
540 *
541 * @see #getDepth
542 * @param depth The number of elements to keep.
543 */
544 public static void trim(final int depth) {
545 contextStack.trim(depth);
546 }
547
548 /**
549 * The ThreadContext Stack interface.
550 */
551 public interface ContextStack extends Serializable, Collection<String> {
552
553 /**
554 * Returns the element at the top of the stack.
555 *
556 * @return The element at the top of the stack.
557 * @throws java.util.NoSuchElementException if the stack is empty.
558 */
559 String pop();
560
561 /**
562 * Returns the element at the top of the stack without removing it or null if the stack is empty.
563 *
564 * @return the element at the top of the stack or null if the stack is empty.
565 */
566 String peek();
567
568 /**
569 * Pushes an element onto the stack.
570 *
571 * @param message The element to add.
572 */
573 void push(String message);
574
575 /**
576 * Returns the number of elements in the stack.
577 *
578 * @return the number of elements in the stack.
579 */
580 int getDepth();
581
582 /**
583 * Returns all the elements in the stack in a List.
584 *
585 * @return all the elements in the stack in a List.
586 */
587 List<String> asList();
588
589 /**
590 * Trims elements from the end of the stack.
591 *
592 * @param depth The maximum number of items in the stack to keep.
593 */
594 void trim(int depth);
595
596 /**
597 * Returns a copy of the ContextStack.
598 *
599 * @return a copy of the ContextStack.
600 */
601 ContextStack copy();
602
603 /**
604 * Returns a ContextStack with the same contents as this ContextStack or {@code null}. Attempts to modify the
605 * returned stack may or may not throw an exception, but will not affect the contents of this ContextStack.
606 *
607 * @return a ContextStack with the same contents as this ContextStack or {@code null}.
608 */
609 ContextStack getImmutableStackOrNull();
610 }
611 }