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(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(int depth) {
085                // Do nothing
086            }
087    
088            @Override
089            public boolean equals(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(T[] a) {
107                throw new UnsupportedOperationException();
108            }
109    
110            @Override
111            public boolean add(String e) {
112                throw new UnsupportedOperationException();
113            }
114    
115            @Override
116            public boolean containsAll(Collection<?> c) {
117                return false;
118            }
119    
120            @Override
121            public boolean addAll(Collection<? extends String> c) {
122                throw new UnsupportedOperationException();
123            }
124    
125            @Override
126            public boolean removeAll(Collection<?> c) {
127                throw new UnsupportedOperationException();
128            }
129    
130            @Override
131            public boolean retainAll(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        }
146    
147        /**
148         * An empty iterator. Since Java 1.7 added the Collections.emptyIterator() method, we have to make do.
149         * @param <E> the type of the empty iterator
150         */
151        private static class EmptyIterator<E> implements Iterator<E> {
152    
153            @Override
154            public boolean hasNext() {
155                return false;
156            }
157    
158            @Override
159            public E next() {
160                throw new NoSuchElementException("This is an empty iterator!");
161            }
162    
163            @Override
164            public void remove() {
165                // no-op
166            }
167        }
168    
169        /**
170         * Empty, immutable Map.
171         */
172        @SuppressWarnings("PublicStaticCollectionField")
173        public static final Map<String, String> EMPTY_MAP = Collections.emptyMap();
174    
175        /**
176         * Empty, immutable ContextStack.
177         */
178        @SuppressWarnings("PublicStaticCollectionField")
179        public static final ThreadContextStack EMPTY_STACK = new EmptyThreadContextStack();
180    
181        private static final String DISABLE_MAP = "disableThreadContextMap";
182        private static final String DISABLE_STACK = "disableThreadContextStack";
183        private static final String DISABLE_ALL = "disableThreadContext";
184        private static final String THREAD_CONTEXT_KEY = "log4j2.threadContextMap";
185    
186        private static boolean disableAll;
187        private static boolean useMap;
188        private static boolean useStack;
189        private static ThreadContextMap contextMap;
190        private static ThreadContextStack contextStack;
191        private static final Logger LOGGER = StatusLogger.getLogger();
192    
193        static {
194            init();
195        }
196            
197        /**
198         * <em>Consider private, used for testing.</em>
199         */
200        static void init() {
201            contextMap = null;
202            final PropertiesUtil managerProps = PropertiesUtil.getProperties();
203            disableAll = managerProps.getBooleanProperty(DISABLE_ALL);
204            useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || disableAll);
205            useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || disableAll);
206    
207            contextStack = new DefaultThreadContextStack(useStack);
208            String threadContextMapName = managerProps.getStringProperty(THREAD_CONTEXT_KEY);
209            final ClassLoader cl = ProviderUtil.findClassLoader();
210            if (threadContextMapName != null) {
211                try {
212                    final Class<?> clazz = cl.loadClass(threadContextMapName);
213                    if (ThreadContextMap.class.isAssignableFrom(clazz)) {
214                        contextMap = (ThreadContextMap) clazz.newInstance();
215                    }
216                } catch (final ClassNotFoundException cnfe) {
217                    LOGGER.error("Unable to locate configured LoggerContextFactory {}", threadContextMapName);
218                } catch (final Exception ex) {
219                    LOGGER.error("Unable to create configured LoggerContextFactory {}", threadContextMapName, ex);
220                }
221            }
222            if (contextMap == null && ProviderUtil.hasProviders()) {
223                final LoggerContextFactory factory = LogManager.getFactory();
224                for (final Provider provider : ProviderUtil.getProviders()) {
225                    threadContextMapName = provider.getThreadContextMap();
226                    final String factoryClassName = provider.getClassName();
227                    if (threadContextMapName != null && factory.getClass().getName().equals(factoryClassName)) {
228                        try {
229                            final Class<?> clazz = cl.loadClass(threadContextMapName);
230                            if (ThreadContextMap.class.isAssignableFrom(clazz)) {
231                                contextMap = (ThreadContextMap) clazz.newInstance();
232                                break;
233                            }
234                        } catch (final ClassNotFoundException cnfe) {
235                            LOGGER.error("Unable to locate configured LoggerContextFactory {}", threadContextMapName);
236                            contextMap = new DefaultThreadContextMap(useMap);
237                        } catch (final Exception ex) {
238                            LOGGER.error("Unable to create configured LoggerContextFactory {}", threadContextMapName, ex);
239                            contextMap = new DefaultThreadContextMap(useMap);
240                        }
241                    }
242                }
243            }
244            if (contextMap == null) {
245                contextMap = new DefaultThreadContextMap(useMap);
246            }
247        }
248    
249        private ThreadContext() {
250            // empty
251        }
252    
253        /**
254         * Puts a context value (the <code>value</code> parameter) as identified
255         * with the <code>key</code> parameter into the current thread's
256         * context map.
257         * <p/>
258         * <p>If the current thread does not have a context map it is
259         * created as a side effect.
260         * @param key The key name.
261         * @param value The key value.
262         */
263        public static void put(final String key, final String value) {
264            contextMap.put(key, value);
265        }
266    
267        /**
268         * Gets the context value identified by the <code>key</code> parameter.
269         * <p/>
270         * <p>This method has no side effects.
271         * @param key The key to locate.
272         * @return The value associated with the key or null.
273         */
274        public static String get(final String key) {
275            return contextMap.get(key);
276        }
277    
278        /**
279         * Removes the context value identified by the <code>key</code> parameter.
280         * @param key The key to remove.
281         */
282        public static void remove(final String key) {
283            contextMap.remove(key);
284        }
285    
286        /**
287         * Clears the context map.
288         */
289        public static void clearMap() {
290            contextMap.clear();
291        }
292    
293        /**
294         * Clears the context map and stack.
295         */
296        public static void clearAll() {
297            clearMap();
298            clearStack();
299        }
300    
301        /**
302         * Determines if the key is in the context.
303         * @param key The key to locate.
304         * @return True if the key is in the context, false otherwise.
305         */
306        public static boolean containsKey(final String key) {
307            return contextMap.containsKey(key);
308        }
309    
310        /**
311         * Returns a mutable copy of current thread's context Map.
312         * @return a mutable copy of the context.
313         */
314        public static Map<String, String> getContext() {
315            return contextMap.getCopy();
316        }
317    
318        /**
319         * Returns an immutable view of the current thread's context Map.
320         * @return An immutable view of the ThreadContext Map.
321         */
322        public static Map<String, String> getImmutableContext() {
323            final Map<String, String> map = contextMap.getImmutableMapOrNull();
324            return map == null ? EMPTY_MAP : map;
325        }
326    
327        /**
328         * Returns true if the Map is empty.
329         * @return true if the Map is empty, false otherwise.
330         */
331        public static boolean isEmpty() {
332            return contextMap.isEmpty();
333        }
334    
335        /**
336         * Clears the stack for this thread.
337         */
338        public static void clearStack() {
339            contextStack.clear();
340        }
341    
342        /**
343         * Returns a copy of this thread's stack.
344         * @return A copy of this thread's stack.
345         */
346        public static ContextStack cloneStack() {
347            return contextStack.copy();
348        }
349    
350        /**
351         * Gets an immutable copy of this current thread's context stack.
352         * @return an immutable copy of the ThreadContext stack.
353         */
354        public static ContextStack getImmutableStack() {
355            return contextStack;
356        }
357    
358        /**
359         * Sets this thread's stack.
360         * @param stack The stack to use.
361         */
362        public static void setStack(final Collection<String> stack) {
363            if (stack.isEmpty() || !useStack) {
364                return;
365            }
366            contextStack.clear();
367            contextStack.addAll(stack);
368        }
369    
370        /**
371         * Gets the current nesting depth of this thread's stack.
372         * @return the number of items in the stack.
373         *
374         * @see #trim
375         */
376        public static int getDepth() {
377            return contextStack.getDepth();
378        }
379    
380        /**
381         * Returns the value of the last item placed on the stack.
382         * <p/>
383         * <p>The returned value is the value that was pushed last. If no
384         * context is available, then the empty string "" is returned.
385         *
386         * @return String The innermost diagnostic context.
387         */
388        public static String pop() {
389            return contextStack.pop();
390        }
391    
392        /**
393         * Looks at the last diagnostic context at the top of this NDC
394         * without removing it.
395         * <p/>
396         * <p>The returned value is the value that was pushed last. If no
397         * context is available, then the empty string "" is returned.
398         *
399         * @return String The innermost diagnostic context.
400         */
401        public static String peek() {
402            return contextStack.peek();
403        }
404    
405        /**
406         * Pushes new diagnostic context information for the current thread.
407         * <p/>
408         * <p>The contents of the <code>message</code> parameter is
409         * determined solely by the client.
410         *
411         * @param message The new diagnostic context information.
412         */
413        public static void push(final String message) {
414            contextStack.push(message);
415        }
416        /**
417         * Pushes new diagnostic context information for the current thread.
418         * <p/>
419         * <p>The contents of the <code>message</code> and args parameters are
420         * determined solely by the client. The message will be treated as a format String
421         * and tokens will be replaced with the String value of the arguments in accordance
422         * with ParameterizedMessage.
423         *
424         * @param message The new diagnostic context information.
425         * @param args Parameters for the message.
426         */
427        public static void push(final String message, final Object... args) {
428            contextStack.push(ParameterizedMessage.format(message, args));
429        }
430    
431        /**
432         * Removes the diagnostic context for this thread.
433         * <p/>
434         * <p>Each thread that created a diagnostic context by calling
435         * {@link #push} should call this method before exiting. Otherwise,
436         * the memory used by the <b>thread</b> cannot be reclaimed by the
437         * VM.
438         * <p/>
439         * <p>As this is such an important problem in heavy duty systems and
440         * because it is difficult to always guarantee that the remove
441         * method is called before exiting a thread, this method has been
442         * augmented to lazily remove references to dead threads. In
443         * practice, this means that you can be a little sloppy and
444         * occasionally forget to call {@link #remove} before exiting a
445         * thread. However, you must call <code>remove</code> sometime. If
446         * you never call it, then your application is sure to run out of
447         * memory.
448         */
449        public static void removeStack() {
450            contextStack.clear();
451        }
452    
453        /**
454         * Trims elements from this diagnostic context. If the current
455         * depth is smaller or equal to <code>maxDepth</code>, then no
456         * action is taken. If the current depth is larger than newDepth
457         * then all elements at maxDepth or higher are discarded.
458         * <p/>
459         * <p>This method is a convenient alternative to multiple {@link
460         * #pop} calls. Moreover, it is often the case that at the end of
461         * complex call sequences, the depth of the ThreadContext is
462         * unpredictable. The <code>trim</code> method circumvents
463         * this problem.
464         * <p/>
465         * <p>For example, the combination
466         * <pre>
467         * void foo() {
468         * &nbsp;  int depth = ThreadContext.getDepth();
469         * <p/>
470         * &nbsp;  ... complex sequence of calls
471         * <p/>
472         * &nbsp;  ThreadContext.trim(depth);
473         * }
474         * </pre>
475         * <p/>
476         * ensures that between the entry and exit of foo the depth of the
477         * diagnostic stack is conserved.
478         *
479         * @see #getDepth
480         * @param depth The number of elements to keep.
481         */
482        public static void trim(final int depth) {
483            contextStack.trim(depth);
484        }
485    
486        /**
487         * The ThreadContext Stack interface.
488         */
489        public interface ContextStack extends Serializable, Collection<String> {
490    
491            /**
492             * Returns the element at the top of the stack.
493             * @return The element at the top of the stack.
494             * @throws java.util.NoSuchElementException if the stack is empty.
495             */
496            String pop();
497    
498            /**
499             * Returns the element at the top of the stack without removing it or null if the stack is empty.
500             * @return the element at the top of the stack or null if the stack is empty.
501             */
502            String peek();
503    
504            /**
505             * Pushes an element onto the stack.
506             * @param message The element to add.
507             */
508            void push(String message);
509    
510            /**
511             * Returns the number of elements in the stack.
512             * @return the number of elements in the stack.
513             */
514            int getDepth();
515    
516            /**
517             * Returns all the elements in the stack in a List.
518             * @return all the elements in the stack in a List.
519             */
520            List<String> asList();
521    
522            /**
523             * Trims elements from the end of the stack.
524             * @param depth The maximum number of items in the stack to keep.
525             */
526            void trim(int depth);
527    
528            /**
529             * Returns a copy of the ContextStack.
530             * @return a copy of the ContextStack.s
531             */
532            ContextStack copy();
533        }
534    }