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.ArrayList;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.Iterator;
025import java.util.List;
026import java.util.Map;
027
028import org.apache.logging.log4j.message.ParameterizedMessage;
029import org.apache.logging.log4j.spi.DefaultThreadContextMap;
030import org.apache.logging.log4j.spi.DefaultThreadContextStack;
031import org.apache.logging.log4j.spi.LoggerContextFactory;
032import org.apache.logging.log4j.spi.MutableThreadContextStack;
033import org.apache.logging.log4j.spi.Provider;
034import org.apache.logging.log4j.spi.ThreadContextMap;
035import org.apache.logging.log4j.spi.ThreadContextStack;
036import org.apache.logging.log4j.status.StatusLogger;
037import org.apache.logging.log4j.util.PropertiesUtil;
038import 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 */
047public 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}