001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017
018package org.apache.logging.log4j;
019
020import java.io.Serializable;
021import java.util.AbstractCollection;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.Iterator;
025import java.util.List;
026import java.util.Map;
027import java.util.NoSuchElementException;
028
029import org.apache.logging.log4j.message.ParameterizedMessage;
030import org.apache.logging.log4j.spi.DefaultThreadContextMap;
031import org.apache.logging.log4j.spi.DefaultThreadContextStack;
032import org.apache.logging.log4j.spi.Provider;
033import org.apache.logging.log4j.spi.ThreadContextMap;
034import org.apache.logging.log4j.spi.ThreadContextStack;
035import org.apache.logging.log4j.status.StatusLogger;
036import org.apache.logging.log4j.util.PropertiesUtil;
037import org.apache.logging.log4j.util.ProviderUtil;
038
039/**
040 * The ThreadContext allows applications to store information either in a Map or a Stack.
041 * <p>
042 * <b><em>The MDC is managed on a per thread basis</em></b>. A child thread automatically inherits a <em>copy</em> of
043 * the mapped diagnostic context of its parent.
044 * </p>
045 */
046public final class ThreadContext {
047
048    /**
049     * An empty read-only ThreadContextStack.
050     */
051    private static class EmptyThreadContextStack extends AbstractCollection<String> implements ThreadContextStack {
052
053        private static final long serialVersionUID = 1L;
054
055        private static final Iterator<String> EMPTY_ITERATOR = new EmptyIterator<>();
056
057        @Override
058        public String pop() {
059            return null;
060        }
061
062        @Override
063        public String peek() {
064            return null;
065        }
066
067        @Override
068        public void push(final String message) {
069            throw new UnsupportedOperationException();
070        }
071
072        @Override
073        public int getDepth() {
074            return 0;
075        }
076
077        @Override
078        public List<String> asList() {
079            return Collections.emptyList();
080        }
081
082        @Override
083        public void trim(final int depth) {
084            // Do nothing
085        }
086
087        @Override
088        public boolean equals(final Object o) {
089            // Similar to java.util.Collections.EmptyList.equals(Object)
090            return (o instanceof Collection) && ((Collection<?>) o).isEmpty();
091        }
092
093        @Override
094        public int hashCode() {
095            // Same as java.util.Collections.EmptyList.hashCode()
096            return 1;
097        }
098
099        @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     * 
153     * @param <E> the type of the empty iterator
154     */
155    private static class EmptyIterator<E> implements Iterator<E> {
156
157        @Override
158        public boolean hasNext() {
159            return false;
160        }
161
162        @Override
163        public E next() {
164            throw new NoSuchElementException("This is an empty iterator!");
165        }
166
167        @Override
168        public void remove() {
169            // no-op
170        }
171    }
172
173    /**
174     * Empty, immutable Map.
175     */
176    // ironically, this annotation gives an "unsupported @SuppressWarnings" warning in Eclipse
177    @SuppressWarnings("PublicStaticCollectionField")
178    // I like irony, so I won't delete it...
179    public static final Map<String, String> EMPTY_MAP = Collections.emptyMap();
180
181    /**
182     * Empty, immutable ContextStack.
183     */
184    // ironically, this annotation gives an "unsupported @SuppressWarnings" warning in Eclipse
185    @SuppressWarnings("PublicStaticCollectionField")
186    public static final ThreadContextStack EMPTY_STACK = new EmptyThreadContextStack();
187
188    private static final String DISABLE_MAP = "disableThreadContextMap";
189    private static final String DISABLE_STACK = "disableThreadContextStack";
190    private static final String DISABLE_ALL = "disableThreadContext";
191    private static final String THREAD_CONTEXT_KEY = "log4j2.threadContextMap";
192
193    private static boolean disableAll;
194    private static boolean useMap;
195    private static boolean useStack;
196    private static ThreadContextMap contextMap;
197    private static ThreadContextStack contextStack;
198    private static final Logger LOGGER = StatusLogger.getLogger();
199
200    static {
201        init();
202    }
203
204    private ThreadContext() {
205        // empty
206    }
207
208    /**
209     * <em>Consider private, used for testing.</em>
210     */
211    static void init() {
212        contextMap = null;
213        final PropertiesUtil managerProps = PropertiesUtil.getProperties();
214        disableAll = managerProps.getBooleanProperty(DISABLE_ALL);
215        useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || disableAll);
216        useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || disableAll);
217
218        contextStack = new DefaultThreadContextStack(useStack);
219        final String threadContextMapName = managerProps.getStringProperty(THREAD_CONTEXT_KEY);
220        final ClassLoader cl = ProviderUtil.findClassLoader();
221        if (threadContextMapName != null) {
222            try {
223                final Class<?> clazz = cl.loadClass(threadContextMapName);
224                if (ThreadContextMap.class.isAssignableFrom(clazz)) {
225                    contextMap = (ThreadContextMap) clazz.newInstance();
226                }
227            } catch (final ClassNotFoundException cnfe) {
228                LOGGER.error("Unable to locate configured ThreadContextMap {}", threadContextMapName);
229            } catch (final Exception ex) {
230                LOGGER.error("Unable to create configured ThreadContextMap {}", threadContextMapName, ex);
231            }
232        }
233        if (contextMap == null && ProviderUtil.hasProviders()) {
234            final String factoryClassName = LogManager.getFactory().getClass().getName();
235            for (final Provider provider : ProviderUtil.getProviders()) {
236                if (factoryClassName.equals(provider.getClassName())) {
237                    final Class<? extends ThreadContextMap> clazz = provider.loadThreadContextMap();
238                    if (clazz != null) {
239                        try {
240                            contextMap = clazz.newInstance();
241                            break;
242                        } catch (final Exception e) {
243                            LOGGER.error("Unable to locate or load configured ThreadContextMap {}",
244                                    provider.getThreadContextMap(), e);
245                            contextMap = new DefaultThreadContextMap(useMap);
246                        }
247                    }
248                }
249            }
250        }
251        if (contextMap == null) {
252            contextMap = new DefaultThreadContextMap(useMap);
253        }
254    }
255
256    /**
257     * Puts a context value (the <code>value</code> parameter) as identified with the <code>key</code> parameter into
258     * the current thread's context map.
259     *
260     * <p>
261     * If the current thread does not have a context map it is created as a side effect.
262     * </p>
263     * 
264     * @param key The key name.
265     * @param value The key value.
266     */
267    public static void put(final String key, final String value) {
268        contextMap.put(key, value);
269    }
270
271    /**
272     * Gets the context value identified by the <code>key</code> parameter.
273     *
274     * <p>
275     * This method has no side effects.
276     * </p>
277     * 
278     * @param key The key to locate.
279     * @return The value associated with the key or null.
280     */
281    public static String get(final String key) {
282        return contextMap.get(key);
283    }
284
285    /**
286     * Removes the context value identified by the <code>key</code> parameter.
287     * 
288     * @param key The key to remove.
289     */
290    public static void remove(final String key) {
291        contextMap.remove(key);
292    }
293
294    /**
295     * Clears the context map.
296     */
297    public static void clearMap() {
298        contextMap.clear();
299    }
300
301    /**
302     * Clears the context map and stack.
303     */
304    public static void clearAll() {
305        clearMap();
306        clearStack();
307    }
308
309    /**
310     * Determines if the key is in the context.
311     * 
312     * @param key The key to locate.
313     * @return True if the key is in the context, false otherwise.
314     */
315    public static boolean containsKey(final String key) {
316        return contextMap.containsKey(key);
317    }
318
319    /**
320     * Returns a mutable copy of current thread's context Map.
321     * 
322     * @return a mutable copy of the context.
323     */
324    public static Map<String, String> getContext() {
325        return contextMap.getCopy();
326    }
327
328    /**
329     * Returns an immutable view of the current thread's context Map.
330     * 
331     * @return An immutable view of the ThreadContext Map.
332     */
333    public static Map<String, String> getImmutableContext() {
334        final Map<String, String> map = contextMap.getImmutableMapOrNull();
335        return map == null ? EMPTY_MAP : map;
336    }
337
338    /**
339     * Returns true if the Map is empty.
340     * 
341     * @return true if the Map is empty, false otherwise.
342     */
343    public static boolean isEmpty() {
344        return contextMap.isEmpty();
345    }
346
347    /**
348     * Clears the stack for this thread.
349     */
350    public static void clearStack() {
351        contextStack.clear();
352    }
353
354    /**
355     * Returns a copy of this thread's stack.
356     * 
357     * @return A copy of this thread's stack.
358     */
359    public static ContextStack cloneStack() {
360        return contextStack.copy();
361    }
362
363    /**
364     * Gets an immutable copy of this current thread's context stack.
365     * 
366     * @return an immutable copy of the ThreadContext stack.
367     */
368    public static ContextStack getImmutableStack() {
369        final ContextStack result = contextStack.getImmutableStackOrNull();
370        return result == null ? EMPTY_STACK : result;
371    }
372
373    /**
374     * Sets this thread's stack.
375     * 
376     * @param stack The stack to use.
377     */
378    public static void setStack(final Collection<String> stack) {
379        if (stack.isEmpty() || !useStack) {
380            return;
381        }
382        contextStack.clear();
383        contextStack.addAll(stack);
384    }
385
386    /**
387     * Gets the current nesting depth of this thread's stack.
388     * 
389     * @return the number of items in the stack.
390     *
391     * @see #trim
392     */
393    public static int getDepth() {
394        return contextStack.getDepth();
395    }
396
397    /**
398     * Returns the value of the last item placed on the stack.
399     *
400     * <p>
401     * The returned value is the value that was pushed last. If no context is available, then the empty string "" is
402     * returned.
403     * </p>
404     *
405     * @return String The innermost diagnostic context.
406     */
407    public static String pop() {
408        return contextStack.pop();
409    }
410
411    /**
412     * Looks at the last diagnostic context at the top of this NDC without removing it.
413     *
414     * <p>
415     * The returned value is the value that was pushed last. If no context is available, then the empty string "" is
416     * returned.
417     * </p>
418     *
419     * @return String The innermost diagnostic context.
420     */
421    public static String peek() {
422        return contextStack.peek();
423    }
424
425    /**
426     * Pushes new diagnostic context information for the current thread.
427     *
428     * <p>
429     * The contents of the <code>message</code> parameter is determined solely by the client.
430     * </p>
431     *
432     * @param message The new diagnostic context information.
433     */
434    public static void push(final String message) {
435        contextStack.push(message);
436    }
437
438    /**
439     * Pushes new diagnostic context information for the current thread.
440     *
441     * <p>
442     * The contents of the <code>message</code> and args parameters are determined solely by the client. The message
443     * will be treated as a format String and tokens will be replaced with the String value of the arguments in
444     * accordance with ParameterizedMessage.
445     * </p>
446     *
447     * @param message The new diagnostic context information.
448     * @param args Parameters for the message.
449     */
450    public static void push(final String message, final Object... args) {
451        contextStack.push(ParameterizedMessage.format(message, args));
452    }
453
454    /**
455     * Removes the diagnostic context for this thread.
456     *
457     * <p>
458     * Each thread that created a diagnostic context by calling {@link #push} should call this method before exiting.
459     * Otherwise, the memory used by the <b>thread</b> cannot be reclaimed by the VM.
460     * </p>
461     *
462     * <p>
463     * As this is such an important problem in heavy duty systems and because it is difficult to always guarantee that
464     * the remove method is called before exiting a thread, this method has been augmented to lazily remove references
465     * to dead threads. In practice, this means that you can be a little sloppy and occasionally forget to call
466     * {@link #remove} before exiting a thread. However, you must call <code>remove</code> sometime. If you never call
467     * it, then your application is sure to run out of memory.
468     * </p>
469     */
470    public static void removeStack() {
471        contextStack.clear();
472    }
473
474    /**
475     * Trims elements from this diagnostic context. If the current depth is smaller or equal to <code>maxDepth</code>,
476     * then no action is taken. If the current depth is larger than newDepth then all elements at maxDepth or higher are
477     * discarded.
478     *
479     * <p>
480     * This method is a convenient alternative to multiple {@link #pop} calls. Moreover, it is often the case that at
481     * the end of complex call sequences, the depth of the ThreadContext is unpredictable. The <code>trim</code> method
482     * circumvents this problem.
483     * </p>
484     *
485     * <p>
486     * For example, the combination
487     * </p>
488     * 
489     * <pre>
490     * void foo() {
491     *     final int depth = ThreadContext.getDepth();
492     * 
493     *     // ... complex sequence of calls
494     * 
495     *     ThreadContext.trim(depth);
496     * }
497     * </pre>
498     *
499     * <p>
500     * ensures that between the entry and exit of {@code foo} the depth of the diagnostic stack is conserved.
501     * </p>
502     *
503     * @see #getDepth
504     * @param depth The number of elements to keep.
505     */
506    public static void trim(final int depth) {
507        contextStack.trim(depth);
508    }
509
510    /**
511     * The ThreadContext Stack interface.
512     */
513    public interface ContextStack extends Serializable, Collection<String> {
514
515        /**
516         * Returns the element at the top of the stack.
517         * 
518         * @return The element at the top of the stack.
519         * @throws java.util.NoSuchElementException if the stack is empty.
520         */
521        String pop();
522
523        /**
524         * Returns the element at the top of the stack without removing it or null if the stack is empty.
525         * 
526         * @return the element at the top of the stack or null if the stack is empty.
527         */
528        String peek();
529
530        /**
531         * Pushes an element onto the stack.
532         * 
533         * @param message The element to add.
534         */
535        void push(String message);
536
537        /**
538         * Returns the number of elements in the stack.
539         * 
540         * @return the number of elements in the stack.
541         */
542        int getDepth();
543
544        /**
545         * Returns all the elements in the stack in a List.
546         * 
547         * @return all the elements in the stack in a List.
548         */
549        List<String> asList();
550
551        /**
552         * Trims elements from the end of the stack.
553         * 
554         * @param depth The maximum number of items in the stack to keep.
555         */
556        void trim(int depth);
557
558        /**
559         * Returns a copy of the ContextStack.
560         * 
561         * @return a copy of the ContextStack.
562         */
563        ContextStack copy();
564
565        /**
566         * Returns a ContextStack with the same contents as this ContextStack or {@code null}. Attempts to modify the
567         * returned stack may or may not throw an exception, but will not affect the contents of this ContextStack.
568         * 
569         * @return a ContextStack with the same contents as this ContextStack or {@code null}.
570         */
571        ContextStack getImmutableStackOrNull();
572    }
573}