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.ArrayList;
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    
028    import org.apache.logging.log4j.message.ParameterizedMessage;
029    import org.apache.logging.log4j.spi.DefaultThreadContextMap;
030    import org.apache.logging.log4j.spi.DefaultThreadContextStack;
031    import org.apache.logging.log4j.spi.LoggerContextFactory;
032    import org.apache.logging.log4j.spi.MutableThreadContextStack;
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         * Empty, immutable Map.
051         */
052        public static final Map<String, String> EMPTY_MAP = Collections.emptyMap();
053    
054        /**
055         * Empty, immutable ContextStack.
056         */
057        public static final ThreadContextStack EMPTY_STACK = new MutableThreadContextStack(new ArrayList<String>());
058    
059        private static final String DISABLE_MAP = "disableThreadContextMap";
060        private static final String DISABLE_STACK = "disableThreadContextStack";
061        private static final String DISABLE_ALL = "disableThreadContext";
062        private static final String THREAD_CONTEXT_KEY = "log4j2.threadContextMap";
063    
064        private static boolean all;
065        private static boolean useMap;
066        private static boolean useStack;
067        private static ThreadContextMap contextMap;
068        private static ThreadContextStack contextStack;
069        private static final Logger LOGGER = StatusLogger.getLogger();
070    
071        static {
072            final PropertiesUtil managerProps = PropertiesUtil.getProperties();
073            all = managerProps.getBooleanProperty(DISABLE_ALL);
074            useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || all);
075            contextStack = new DefaultThreadContextStack(useStack);
076    
077            useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || all);
078            String threadContextMapName = managerProps.getStringProperty(THREAD_CONTEXT_KEY);
079            final ClassLoader cl = ProviderUtil.findClassLoader();
080            if (threadContextMapName != null) {
081                try {
082                    final Class<?> clazz = cl.loadClass(threadContextMapName);
083                    if (ThreadContextMap.class.isAssignableFrom(clazz)) {
084                        contextMap = (ThreadContextMap) clazz.newInstance();
085                    }
086                } catch (final ClassNotFoundException cnfe) {
087                    LOGGER.error("Unable to locate configured LoggerContextFactory {}", threadContextMapName);
088                } catch (final Exception ex) {
089                    LOGGER.error("Unable to create configured LoggerContextFactory {}", threadContextMapName, ex);
090                }
091            }
092            if (contextMap == null && ProviderUtil.hasProviders()) {
093                final LoggerContextFactory factory = LogManager.getFactory();
094                final Iterator<Provider> providers = ProviderUtil.getProviders();
095                while (providers.hasNext()) {
096                    final Provider provider = providers.next();
097                    threadContextMapName = provider.getThreadContextMap();
098                    final String factoryClassName = provider.getClassName();
099                    if (threadContextMapName != null && factory.getClass().getName().equals(factoryClassName)) {
100                        try {
101                            final Class<?> clazz = cl.loadClass(threadContextMapName);
102                            if (ThreadContextMap.class.isAssignableFrom(clazz)) {
103                                contextMap = (ThreadContextMap) clazz.newInstance();
104                                break;
105                            }
106                        } catch (final ClassNotFoundException cnfe) {
107                            LOGGER.error("Unable to locate configured LoggerContextFactory {}", threadContextMapName);
108                            contextMap = new DefaultThreadContextMap(useMap);
109                        } catch (final Exception ex) {
110                            LOGGER.error("Unable to create configured LoggerContextFactory {}", threadContextMapName, ex);
111                            contextMap = new DefaultThreadContextMap(useMap);
112                        }
113                    }
114                }
115            }
116            if (contextMap == null) {
117                contextMap = new DefaultThreadContextMap(useMap);
118            }
119        }
120    
121        private ThreadContext() {
122    
123        }
124    
125        /**
126         * Put a context value (the <code>value</code> parameter) as identified
127         * with the <code>key</code> parameter into the current thread's
128         * context map.
129         * <p/>
130         * <p>If the current thread does not have a context map it is
131         * created as a side effect.
132         * @param key The key name.
133         * @param value The key value.
134         */
135        public static void put(final String key, final String value) {
136            contextMap.put(key, value);
137        }
138    
139        /**
140         * Get the context value identified by the <code>key</code> parameter.
141         * <p/>
142         * <p>This method has no side effects.
143         * @param key The key to locate.
144         * @return The value associated with the key or null.
145         */
146        public static String get(final String key) {
147            return contextMap.get(key);
148        }
149    
150        /**
151         * Remove the context value identified by the <code>key</code> parameter.
152         * @param key The key to remove.
153         */
154        public static void remove(final String key) {
155            contextMap.remove(key);
156        }
157    
158        /**
159         * Clear the context.
160         */
161        public static void clear() {
162            contextMap.clear();
163        }
164    
165        /**
166         * Determine if the key is in the context.
167         * @param key The key to locate.
168         * @return True if the key is in the context, false otherwise.
169         */
170        public static boolean containsKey(final String key) {
171            return contextMap.containsKey(key);
172        }
173    
174        /**
175         * Returns a mutable copy of current thread's context Map.
176         * @return a mutable copy of the context.
177         */
178        public static Map<String, String> getContext() {
179            return contextMap.getCopy();
180        }
181    
182        /**
183         * Returns an immutable view of the current thread's context Map.
184         * @return An immutable view of the ThreadContext Map.
185         */
186        public static Map<String, String> getImmutableContext() {
187            final Map<String, String> map = contextMap.getImmutableMapOrNull();
188            return map == null ? EMPTY_MAP : map;
189        }
190    
191        /**
192         * Returns true if the Map is empty.
193         * @return true if the Map is empty, false otherwise.
194         */
195        public static boolean isEmpty() {
196            return contextMap.isEmpty();
197        }
198    
199        /**
200         * Clear the stack for this thread.
201         */
202        public static void clearStack() {
203            contextStack.clear();
204        }
205    
206        /**
207         * Returns a copy of this thread's stack.
208         * @return A copy of this thread's stack.
209         */
210        public static ContextStack cloneStack() {
211            return contextStack.copy();
212        }
213    
214        /**
215         * Get an immutable copy of this current thread's context stack.
216         * @return an immutable copy of the ThreadContext stack.
217         */
218        public static ContextStack getImmutableStack() {
219            return contextStack;
220        }
221    
222        /**
223         * Set this thread's stack.
224         * @param stack The stack to use.
225         */
226        public static void setStack(final Collection<String> stack) {
227            if (stack.size() == 0 || !useStack) {
228                return;
229            }
230            contextStack.clear();
231            contextStack.addAll(stack);
232        }
233    
234        /**
235         * Get the current nesting depth of this thread's stack.
236         * @return the number of items in the stack.
237         *
238         * @see #trim
239         */
240        public static int getDepth() {
241            return contextStack.getDepth();
242        }
243    
244        /**
245         * Returns the value of the last item placed on the stack.
246         * <p/>
247         * <p>The returned value is the value that was pushed last. If no
248         * context is available, then the empty string "" is returned.
249         *
250         * @return String The innermost diagnostic context.
251         */
252        public static String pop() {
253            return contextStack.pop();
254        }
255    
256        /**
257         * Looks at the last diagnostic context at the top of this NDC
258         * without removing it.
259         * <p/>
260         * <p>The returned value is the value that was pushed last. If no
261         * context is available, then the empty string "" is returned.
262         *
263         * @return String The innermost diagnostic context.
264         */
265        public static String peek() {
266            return contextStack.peek();
267        }
268    
269        /**
270         * Push new diagnostic context information for the current thread.
271         * <p/>
272         * <p>The contents of the <code>message</code> parameter is
273         * determined solely by the client.
274         *
275         * @param message The new diagnostic context information.
276         */
277        public static void push(final String message) {
278            contextStack.push(message);
279        }
280        /**
281         * Push new diagnostic context information for the current thread.
282         * <p/>
283         * <p>The contents of the <code>message</code> and args parameters are
284         * determined solely by the client. The message will be treated as a format String
285         * and tokens will be replaced with the String value of the arguments in accordance
286         * with ParameterizedMessage.
287         *
288         * @param message The new diagnostic context information.
289         * @param args Parameters for the message.
290         */
291        public static void push(final String message, final Object... args) {
292            contextStack.push(ParameterizedMessage.format(message, args));
293        }
294    
295        /**
296         * Remove the diagnostic context for this thread.
297         * <p/>
298         * <p>Each thread that created a diagnostic context by calling
299         * {@link #push} should call this method before exiting. Otherwise,
300         * the memory used by the <b>thread</b> cannot be reclaimed by the
301         * VM.
302         * <p/>
303         * <p>As this is such an important problem in heavy duty systems and
304         * because it is difficult to always guarantee that the remove
305         * method is called before exiting a thread, this method has been
306         * augmented to lazily remove references to dead threads. In
307         * practice, this means that you can be a little sloppy and
308         * occasionally forget to call {@link #remove} before exiting a
309         * thread. However, you must call <code>remove</code> sometime. If
310         * you never call it, then your application is sure to run out of
311         * memory.
312         */
313        public static void removeStack() {
314            contextStack.clear();
315        }
316    
317        /**
318         * Trims elements from this diagnostic context. If the current
319         * depth is smaller or equal to <code>maxDepth</code>, then no
320         * action is taken. If the current depth is larger than newDepth
321         * then all elements at maxDepth or higher are discarded.
322         * <p/>
323         * <p>This method is a convenient alternative to multiple {@link
324         * #pop} calls. Moreover, it is often the case that at the end of
325         * complex call sequences, the depth of the ThreadContext is
326         * unpredictable. The <code>trim</code> method circumvents
327         * this problem.
328         * <p/>
329         * <p>For example, the combination
330         * <pre>
331         * void foo() {
332         * &nbsp;  int depth = ThreadContext.getDepth();
333         * <p/>
334         * &nbsp;  ... complex sequence of calls
335         * <p/>
336         * &nbsp;  ThreadContext.trim(depth);
337         * }
338         * </pre>
339         * <p/>
340         * ensures that between the entry and exit of foo the depth of the
341         * diagnostic stack is conserved.
342         *
343         * @see #getDepth
344         * @param depth The number of elements to keep.
345         */
346        public static void trim(final int depth) {
347            contextStack.trim(depth);
348        }
349    
350        /**
351         * The ThreadContext Stack interface.
352         */
353        public interface ContextStack extends Serializable {
354    
355            /**
356             * Clears all elements from the stack.
357             */
358            void clear();
359    
360            /**
361             * Returns the element at the top of the stack.
362             * @return The element at the top of the stack.
363             * @throws java.util.NoSuchElementException if the stack is empty.
364             */
365            String pop();
366    
367            /**
368             * Returns the element at the top of the stack without removing it or null if the stack is empty.
369             * @return the element at the top of the stack or null if the stack is empty.
370             */
371            String peek();
372    
373            /**
374             * Add an element to the stack.
375             * @param message The element to add.
376             */
377            void push(String message);
378    
379            /**
380             * Returns the number of elements in the stack.
381             * @return the number of elements in the stack.
382             */
383            int getDepth();
384    
385            /**
386             * Returns all the elements in the stack in a List.
387             * @return all the elements in the stack in a List.
388             */
389            List<String> asList();
390    
391            /**
392             * Trims elements from the end of the stack.
393             * @param depth The maximum number of items in the stack to keep.
394             */
395            void trim(int depth);
396    
397            /**
398             * Returns a copy of the ContextStack.
399             * @return a copy of the ContextStack.s
400             */
401            ContextStack copy();
402        }
403    }