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