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