All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.logging.log4j.ThreadContext Maven / Gradle / Ivy

Go to download

Pax Logging API Library is a collection of logging APIs from different libraries/facades. It supports SLF4J, Commons Logging, JULI Logging, Log4J1 API, Log4J2 API, JBoss Logging and Avalon APIs. Additionally, Pax Logging specific library is available as backend implementation with its specific configuration mechanisms, but it's not required.

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache license, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the license for the specific language governing permissions and
 * limitations under the license.
 */

package org.apache.logging.log4j;

import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.spi.DefaultThreadContextMap;
import org.apache.logging.log4j.spi.DefaultThreadContextStack;
import org.apache.logging.log4j.spi.NoOpThreadContextMap;
import org.apache.logging.log4j.spi.ReadOnlyThreadContextMap;
import org.apache.logging.log4j.spi.ThreadContextMap;
import org.apache.logging.log4j.spi.ThreadContextMap2;
import org.apache.logging.log4j.spi.CleanableThreadContextMap;
import org.apache.logging.log4j.spi.ThreadContextMapFactory;
import org.apache.logging.log4j.spi.ThreadContextStack;
import org.apache.logging.log4j.util.PropertiesUtil;
import org.ops4j.pax.logging.log4jv2.Log4jv2ThreadContextMap;
import org.ops4j.pax.logging.log4jv2.Log4jv2ThreadContextStack;

/**
 * The ThreadContext allows applications to store information either in a Map or a Stack.
 * 

* The MDC is managed on a per thread basis. To enable automatic inheritance of copies of the MDC * to newly created threads, enable the {@value org.apache.logging.log4j.spi.DefaultThreadContextMap#INHERITABLE_MAP} * Log4j system property. *

* @see Thread Context Manual */ public final class ThreadContext { /** * An empty read-only ThreadContextStack. */ private static class EmptyThreadContextStack extends AbstractCollection implements ThreadContextStack { private static final long serialVersionUID = 1L; private static final Iterator EMPTY_ITERATOR = new EmptyIterator<>(); @Override public String pop() { return null; } @Override public String peek() { return null; } @Override public void push(final String message) { throw new UnsupportedOperationException(); } @Override public int getDepth() { return 0; } @Override public List asList() { return Collections.emptyList(); } @Override public void trim(final int depth) { // Do nothing } @Override public boolean equals(final Object o) { // Similar to java.util.Collections.EmptyList.equals(Object) return (o instanceof Collection) && ((Collection) o).isEmpty(); } @Override public int hashCode() { // Same as java.util.Collections.EmptyList.hashCode() return 1; } @Override public ContextStack copy() { return this; } @Override public T[] toArray(final T[] a) { throw new UnsupportedOperationException(); } @Override public boolean add(final String e) { throw new UnsupportedOperationException(); } @Override public boolean containsAll(final Collection c) { return false; } @Override public boolean addAll(final Collection c) { throw new UnsupportedOperationException(); } @Override public boolean removeAll(final Collection c) { throw new UnsupportedOperationException(); } @Override public boolean retainAll(final Collection c) { throw new UnsupportedOperationException(); } @Override public Iterator iterator() { return EMPTY_ITERATOR; } @Override public int size() { return 0; } @Override public ContextStack getImmutableStackOrNull() { return this; } } /** * An empty iterator. Since Java 1.7 added the Collections.emptyIterator() method, we have to make do. * * @param the type of the empty iterator */ private static class EmptyIterator implements Iterator { @Override public boolean hasNext() { return false; } @Override public E next() { throw new NoSuchElementException("This is an empty iterator!"); } @Override public void remove() { // no-op } } /** * Empty, immutable Map. */ // ironically, this annotation gives an "unsupported @SuppressWarnings" warning in Eclipse @SuppressWarnings("PublicStaticCollectionField") // I like irony, so I won't delete it... public static final Map EMPTY_MAP = Collections.emptyMap(); /** * Empty, immutable ContextStack. */ // ironically, this annotation gives an "unsupported @SuppressWarnings" warning in Eclipse @SuppressWarnings("PublicStaticCollectionField") public static final ThreadContextStack EMPTY_STACK = new EmptyThreadContextStack(); private static final String DISABLE_MAP = "disableThreadContextMap"; private static final String DISABLE_STACK = "disableThreadContextStack"; private static final String DISABLE_ALL = "disableThreadContext"; private static boolean useStack; private static ThreadContextMap contextMap; private static ThreadContextStack contextStack; private static ReadOnlyThreadContextMap readOnlyContextMap; static { init(); } private ThreadContext() { // empty } /** * Consider private, used for testing. */ static void init() { contextMap = null; final PropertiesUtil managerProps = PropertiesUtil.getProperties(); boolean disableAll = managerProps.getBooleanProperty(DISABLE_ALL); useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || disableAll); boolean useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || disableAll); contextStack = new Log4jv2ThreadContextStack(useStack); if (!useMap) { contextMap = new NoOpThreadContextMap(); } else { contextMap = new Log4jv2ThreadContextMap(); } if (contextMap instanceof ReadOnlyThreadContextMap) { readOnlyContextMap = (ReadOnlyThreadContextMap) contextMap; } else { readOnlyContextMap = null; } } /** * Puts a context value (the value parameter) as identified with the key parameter into * the current thread's context map. * *

* If the current thread does not have a context map it is created as a side effect. *

* * @param key The key name. * @param value The key value. */ public static void put(final String key, final String value) { contextMap.put(key, value); } /** * Puts a context value (the value parameter) as identified with the key parameter into * the current thread's context map if the key does not exist. * *

* If the current thread does not have a context map it is created as a side effect. *

* * @param key The key name. * @param value The key value. * @since 2.13.0 */ public static void putIfNull(final String key, final String value) { if(!contextMap.containsKey(key)) { contextMap.put(key, value); } } /** * Puts all given context map entries into the current thread's * context map. * *

If the current thread does not have a context map it is * created as a side effect.

* @param m The map. * @since 2.7 */ public static void putAll(final Map m) { if (contextMap instanceof ThreadContextMap2) { ((ThreadContextMap2) contextMap).putAll(m); } else if (contextMap instanceof DefaultThreadContextMap) { ((DefaultThreadContextMap) contextMap).putAll(m); } else { for (final Map.Entry entry: m.entrySet()) { contextMap.put(entry.getKey(), entry.getValue()); } } } /** * Gets the context value identified by the key parameter. * *

* This method has no side effects. *

* * @param key The key to locate. * @return The value associated with the key or null. */ public static String get(final String key) { return contextMap.get(key); } /** * Removes the context value identified by the key parameter. * * @param key The key to remove. */ public static void remove(final String key) { contextMap.remove(key); } /** * Removes the context values identified by the keys parameter. * * @param keys The keys to remove. * * @since 2.8 */ public static void removeAll(final Iterable keys) { if (contextMap instanceof CleanableThreadContextMap) { ((CleanableThreadContextMap) contextMap).removeAll(keys); } else if (contextMap instanceof DefaultThreadContextMap) { ((DefaultThreadContextMap) contextMap).removeAll(keys); } else { for (final String key : keys) { contextMap.remove(key); } } } /** * Clears the context map. */ public static void clearMap() { contextMap.clear(); } /** * Clears the context map and stack. */ public static void clearAll() { clearMap(); clearStack(); } /** * Determines if the key is in the context. * * @param key The key to locate. * @return True if the key is in the context, false otherwise. */ public static boolean containsKey(final String key) { return contextMap.containsKey(key); } /** * Returns a mutable copy of current thread's context Map. * * @return a mutable copy of the context. */ public static Map getContext() { return contextMap.getCopy(); } /** * Returns an immutable view of the current thread's context Map. * * @return An immutable view of the ThreadContext Map. */ public static Map getImmutableContext() { final Map map = contextMap.getImmutableMapOrNull(); return map == null ? EMPTY_MAP : map; } /** * Returns a read-only view of the internal data structure used to store thread context key-value pairs, * or {@code null} if the internal data structure does not implement the * {@code ReadOnlyThreadContextMap} interface. *

* The {@link DefaultThreadContextMap} implementation does not implement {@code ReadOnlyThreadContextMap}, so by * default this method returns {@code null}. *

* * @return the internal data structure used to store thread context key-value pairs or {@code null} * @see ThreadContextMapFactory * @see DefaultThreadContextMap * @see org.apache.logging.log4j.spi.CopyOnWriteSortedArrayThreadContextMap * @see org.apache.logging.log4j.spi.GarbageFreeSortedArrayThreadContextMap * @since 2.8 */ public static ReadOnlyThreadContextMap getThreadContextMap() { return readOnlyContextMap; } /** * Returns true if the Map is empty. * * @return true if the Map is empty, false otherwise. */ public static boolean isEmpty() { return contextMap.isEmpty(); } /** * Clears the stack for this thread. */ public static void clearStack() { contextStack.clear(); } /** * Returns a copy of this thread's stack. * * @return A copy of this thread's stack. */ public static ContextStack cloneStack() { return contextStack.copy(); } /** * Gets an immutable copy of this current thread's context stack. * * @return an immutable copy of the ThreadContext stack. */ public static ContextStack getImmutableStack() { final ContextStack result = contextStack.getImmutableStackOrNull(); return result == null ? EMPTY_STACK : result; } /** * Sets this thread's stack. * * @param stack The stack to use. */ public static void setStack(final Collection stack) { if (stack.isEmpty() || !useStack) { return; } contextStack.clear(); contextStack.addAll(stack); } /** * Gets the current nesting depth of this thread's stack. * * @return the number of items in the stack. * * @see #trim */ public static int getDepth() { return contextStack.getDepth(); } /** * Returns the value of the last item placed on the stack. * *

* The returned value is the value that was pushed last. If no context is available, then the empty string "" is * returned. *

* * @return String The innermost diagnostic context. */ public static String pop() { return contextStack.pop(); } /** * Looks at the last diagnostic context at the top of this NDC without removing it. * *

* The returned value is the value that was pushed last. If no context is available, then the empty string "" is * returned. *

* * @return String The innermost diagnostic context. */ public static String peek() { return contextStack.peek(); } /** * Pushes new diagnostic context information for the current thread. * *

* The contents of the message parameter is determined solely by the client. *

* * @param message The new diagnostic context information. */ public static void push(final String message) { contextStack.push(message); } /** * Pushes new diagnostic context information for the current thread. * *

* The contents of the message and args parameters are determined solely by the client. The message * will be treated as a format String and tokens will be replaced with the String value of the arguments in * accordance with ParameterizedMessage. *

* * @param message The new diagnostic context information. * @param args Parameters for the message. */ public static void push(final String message, final Object... args) { contextStack.push(ParameterizedMessage.format(message, args)); } /** * Removes the diagnostic context for this thread. * *

* Each thread that created a diagnostic context by calling {@link #push} should call this method before exiting. * Otherwise, the memory used by the thread cannot be reclaimed by the VM. *

* *

* As this is such an important problem in heavy duty systems and because it is difficult to always guarantee that * the remove method is called before exiting a thread, this method has been augmented to lazily remove references * to dead threads. In practice, this means that you can be a little sloppy and occasionally forget to call * {@link #remove} before exiting a thread. However, you must call remove sometime. If you never call * it, then your application is sure to run out of memory. *

*/ public static void removeStack() { contextStack.clear(); } /** * Trims elements from this diagnostic context. If the current depth is smaller or equal to maxDepth, * then no action is taken. If the current depth is larger than newDepth then all elements at maxDepth or higher are * discarded. * *

* This method is a convenient alternative to multiple {@link #pop} calls. Moreover, it is often the case that at * the end of complex call sequences, the depth of the ThreadContext is unpredictable. The trim method * circumvents this problem. *

* *

* For example, the combination *

* *
     * void foo() {
     *     final int depth = ThreadContext.getDepth();
     *
     *     // ... complex sequence of calls
     *
     *     ThreadContext.trim(depth);
     * }
     * 
* *

* ensures that between the entry and exit of {@code foo} the depth of the diagnostic stack is conserved. *

* * @see #getDepth * @param depth The number of elements to keep. */ public static void trim(final int depth) { contextStack.trim(depth); } /** * The ThreadContext Stack interface. */ public interface ContextStack extends Serializable, Collection { /** * Returns the element at the top of the stack. * * @return The element at the top of the stack. * @throws java.util.NoSuchElementException if the stack is empty. */ String pop(); /** * Returns the element at the top of the stack without removing it or null if the stack is empty. * * @return the element at the top of the stack or null if the stack is empty. */ String peek(); /** * Pushes an element onto the stack. * * @param message The element to add. */ void push(String message); /** * Returns the number of elements in the stack. * * @return the number of elements in the stack. */ int getDepth(); /** * Returns all the elements in the stack in a List. * * @return all the elements in the stack in a List. */ List asList(); /** * Trims elements from the end of the stack. * * @param depth The maximum number of items in the stack to keep. */ void trim(int depth); /** * Returns a copy of the ContextStack. * * @return a copy of the ContextStack. */ ContextStack copy(); /** * Returns a ContextStack with the same contents as this ContextStack or {@code null}. Attempts to modify the * returned stack may or may not throw an exception, but will not affect the contents of this ContextStack. * * @return a ContextStack with the same contents as this ContextStack or {@code null}. */ ContextStack getImmutableStackOrNull(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy