View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements. See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache license, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License. You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the license for the specific language governing permissions and
    15   * limitations under the license.
    16   */
    17
    18  package org.apache.logging.log4j;
    19
    20  import java.io.Serializable;
    21  import java.util.AbstractCollection;
    22  import java.util.Collection;
    23  import java.util.Collections;
    24  import java.util.Iterator;
    25  import java.util.List;
    26  import java.util.Map;
    27  import java.util.NoSuchElementException;
    28
    29  import org.apache.logging.log4j.message.ParameterizedMessage;
    30  import org.apache.logging.log4j.spi.DefaultThreadContextMap;
    31  import org.apache.logging.log4j.spi.DefaultThreadContextStack;
    32  import org.apache.logging.log4j.spi.Provider;
    33  import org.apache.logging.log4j.spi.ThreadContextMap;
    34  import org.apache.logging.log4j.spi.ThreadContextStack;
    35  import org.apache.logging.log4j.status.StatusLogger;
    36  import org.apache.logging.log4j.util.PropertiesUtil;
    37  import org.apache.logging.log4j.util.ProviderUtil;
    38
    39  /**
    40   * The ThreadContext allows applications to store information either in a Map or a Stack.
    41   * <p>
    42   * <b><em>The MDC is managed on a per thread basis</em></b>. A child thread automatically inherits a <em>copy</em> of
    43   * the mapped diagnostic context of its parent.
    44   * </p>
    45   */
    46  public final class ThreadContext  {
    47
    48      /**
    49       * An empty read-only ThreadContextStack.
    50       */
    51      private static class EmptyThreadContextStack extends AbstractCollection<String> implements ThreadContextStack {
    52
    53          private static final long serialVersionUID = 1L;
    54
    55          private static final Iterator<String> EMPTY_ITERATOR = new EmptyIterator<String>();
    56
    57          @Override
    58          public String pop() {
    59              return null;
    60          }
    61
    62          @Override
    63          public String peek() {
    64              return null;
    65          }
    66
    67          @Override
    68          public void push(final String message) {
    69              throw new UnsupportedOperationException();
    70          }
    71
    72          @Override
    73          public int getDepth() {
    74              return 0;
    75          }
    76
    77          @Override
    78          public List<String> asList() {
    79              return Collections.emptyList();
    80          }
    81
    82          @Override
    83          public void trim(final int depth) {
    84              // Do nothing
    85          }
    86
    87          @Override
    88          public boolean equals(final Object o) {
    89              // Similar to java.util.Collections.EmptyList.equals(Object)
    90              return (o instanceof Collection) && ((Collection<?>) o).isEmpty();
    91          }
    92
    93          @Override
    94          public int hashCode() {
    95              // Same as java.util.Collections.EmptyList.hashCode()
    96              return 1;
    97          }
    98
    99          @Override
    100         public ContextStack copy() {
    101             return this;
    102         }
    103
    104         @Override
    105         public <T> T[] toArray(final T[] a) {
    106             throw new UnsupportedOperationException();
    107         }
    108
    109         @Override
    110         public boolean add(final String e) {
    111             throw new UnsupportedOperationException();
    112         }
    113
    114         @Override
    115         public boolean containsAll(final Collection<?> c) {
    116             return false;
    117         }
    118
    119         @Override
    120         public boolean addAll(final Collection<? extends String> c) {
    121             throw new UnsupportedOperationException();
    122         }
    123
    124         @Override
    125         public boolean removeAll(final Collection<?> c) {
    126             throw new UnsupportedOperationException();
    127         }
    128
    129         @Override
    130         public boolean retainAll(final Collection<?> c) {
    131             throw new UnsupportedOperationException();
    132         }
    133
    134         @Override
    135         public Iterator<String> iterator() {
    136             return EMPTY_ITERATOR;
    137         }
    138
    139         @Override
    140         public int size() {
    141             return 0;
    142         }
    143
    144         @Override
    145         public ContextStack getImmutableStackOrNull() {
    146             return this;
    147         }
    148     }
    149
    150     /**
    151      * An empty iterator. Since Java 1.7 added the Collections.emptyIterator() method, we have to make do.
    152      * @param <E> the type of the empty iterator
    153      */
    154     private static class EmptyIterator<E> implements Iterator<E> {
    155
    156         @Override
    157         public boolean hasNext() {
    158             return false;
    159         }
    160
    161         @Override
    162         public E next() {
    163             throw new NoSuchElementException("This is an empty iterator!");
    164         }
    165
    166         @Override
    167         public void remove() {
    168             // no-op
    169         }
    170     }
    171
    172     /**
    173      * Empty, immutable Map.
    174      */
    175     // ironically, this annotation gives an "unsupported @SuppressWarnings" warning in Eclipse
    176     @SuppressWarnings("PublicStaticCollectionField") // I like irony, so I won't delete it...
    177     public static final Map<String, String> EMPTY_MAP = Collections.emptyMap();
    178
    179     /**
    180      * Empty, immutable ContextStack.
    181      */
    182     // ironically, this annotation gives an "unsupported @SuppressWarnings" warning in Eclipse
    183     @SuppressWarnings("PublicStaticCollectionField")
    184     public static final ThreadContextStack EMPTY_STACK = new EmptyThreadContextStack();
    185
    186     private static final String DISABLE_MAP = "disableThreadContextMap";
    187     private static final String DISABLE_STACK = "disableThreadContextStack";
    188     private static final String DISABLE_ALL = "disableThreadContext";
    189     private static final String THREAD_CONTEXT_KEY = "log4j2.threadContextMap";
    190
    191     private static boolean disableAll;
    192     private static boolean useMap;
    193     private static boolean useStack;
    194     private static ThreadContextMap contextMap;
    195     private static ThreadContextStack contextStack;
    196     private static final Logger LOGGER = StatusLogger.getLogger();
    197
    198     static {
    199         init();
    200     }
    201
    202     /**
    203      * <em>Consider private, used for testing.</em>
    204      */
    205     static void init() {
    206         contextMap = null;
    207         final PropertiesUtil managerProps = PropertiesUtil.getProperties();
    208         disableAll = managerProps.getBooleanProperty(DISABLE_ALL);
    209         useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || disableAll);
    210         useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || disableAll);
    211
    212         contextStack = new DefaultThreadContextStack(useStack);
    213         final String threadContextMapName = managerProps.getStringProperty(THREAD_CONTEXT_KEY);
    214         final ClassLoader cl = ProviderUtil.findClassLoader();
    215         if (threadContextMapName != null) {
    216             try {
    217                 final Class<?> clazz = cl.loadClass(threadContextMapName);
    218                 if (ThreadContextMap.class.isAssignableFrom(clazz)) {
    219                     contextMap = (ThreadContextMap) clazz.newInstance();
    220                 }
    221             } catch (final ClassNotFoundException cnfe) {
    222                 LOGGER.error("Unable to locate configured ThreadContextMap {}", threadContextMapName);
    223             } catch (final Exception ex) {
    224                 LOGGER.error("Unable to create configured ThreadContextMap {}", threadContextMapName, ex);
    225             }
    226         }
    227         if (contextMap == null && ProviderUtil.hasProviders()) {
    228             final String factoryClassName = LogManager.getFactory().getClass().getName();
    229             for (final Provider provider : ProviderUtil.getProviders()) {
    230                 if (factoryClassName.equals(provider.getClassName())) {
    231                     final Class<? extends ThreadContextMap> clazz = provider.loadThreadContextMap();
    232                     if (clazz != null) {
    233                         try {
    234                             contextMap = clazz.newInstance();
    235                             break;
    236                         } catch (final Exception e) {
    237                             LOGGER.error("Unable to locate or load configured ThreadContextMap {}",
    238                                 provider.getThreadContextMap(), e);
    239                             contextMap = new DefaultThreadContextMap(useMap);
    240                         }
    241                     }
    242                 }
    243             }
    244         }
    245         if (contextMap == null) {
    246             contextMap = new DefaultThreadContextMap(useMap);
    247         }
    248     }
    249
    250     private ThreadContext() {
    251         // empty
    252     }
    253
    254     /**
    255      * Puts a context value (the <code>value</code> parameter) as identified
    256      * with the <code>key</code> parameter into the current thread's
    257      * context map.
    258      *
    259      * <p>If the current thread does not have a context map it is
    260      * created as a side effect.</p>
    261      * @param key The key name.
    262      * @param value The key value.
    263      */
    264     public static void put(final String key, final String value) {
    265         contextMap.put(key, value);
    266     }
    267
    268     /**
    269      * Gets the context value identified by the <code>key</code> parameter.
    270      *
    271      * <p>This method has no side effects.</p>
    272      * @param key The key to locate.
    273      * @return The value associated with the key or null.
    274      */
    275     public static String get(final String key) {
    276         return contextMap.get(key);
    277     }
    278
    279     /**
    280      * Removes the context value identified by the <code>key</code> parameter.
    281      * @param key The key to remove.
    282      */
    283     public static void remove(final String key) {
    284         contextMap.remove(key);
    285     }
    286
    287     /**
    288      * Clears the context map.
    289      */
    290     public static void clearMap() {
    291         contextMap.clear();
    292     }
    293
    294     /**
    295      * Clears the context map and stack.
    296      */
    297     public static void clearAll() {
    298         clearMap();
    299         clearStack();
    300     }
    301
    302     /**
    303      * Determines if the key is in the context.
    304      * @param key The key to locate.
    305      * @return True if the key is in the context, false otherwise.
    306      */
    307     public static boolean containsKey(final String key) {
    308         return contextMap.containsKey(key);
    309     }
    310
    311     /**
    312      * Returns a mutable copy of current thread's context Map.
    313      * @return a mutable copy of the context.
    314      */
    315     public static Map<String, String> getContext() {
    316         return contextMap.getCopy();
    317     }
    318
    319     /**
    320      * Returns an immutable view of the current thread's context Map.
    321      * @return An immutable view of the ThreadContext Map.
    322      */
    323     public static Map<String, String> getImmutableContext() {
    324         final Map<String, String> map = contextMap.getImmutableMapOrNull();
    325         return map == null ? EMPTY_MAP : map;
    326     }
    327
    328     /**
    329      * Returns true if the Map is empty.
    330      * @return true if the Map is empty, false otherwise.
    331      */
    332     public static boolean isEmpty() {
    333         return contextMap.isEmpty();
    334     }
    335
    336     /**
    337      * Clears the stack for this thread.
    338      */
    339     public static void clearStack() {
    340         contextStack.clear();
    341     }
    342
    343     /**
    344      * Returns a copy of this thread's stack.
    345      * @return A copy of this thread's stack.
    346      */
    347     public static ContextStack cloneStack() {
    348         return contextStack.copy();
    349     }
    350
    351     /**
    352      * Gets an immutable copy of this current thread's context stack.
    353      * @return an immutable copy of the ThreadContext stack.
    354      */
    355     public static ContextStack getImmutableStack() {
    356         final ContextStack result = contextStack.getImmutableStackOrNull();
    357         return result == null ? EMPTY_STACK : result;
    358     }
    359
    360     /**
    361      * Sets this thread's stack.
    362      * @param stack The stack to use.
    363      */
    364     public static void setStack(final Collection<String> stack) {
    365         if (stack.isEmpty() || !useStack) {
    366             return;
    367         }
    368         contextStack.clear();
    369         contextStack.addAll(stack);
    370     }
    371
    372     /**
    373      * Gets the current nesting depth of this thread's stack.
    374      * @return the number of items in the stack.
    375      *
    376      * @see #trim
    377      */
    378     public static int getDepth() {
    379         return contextStack.getDepth();
    380     }
    381
    382     /**
    383      * Returns the value of the last item placed on the stack.
    384      *
    385      * <p>The returned value is the value that was pushed last. If no
    386      * context is available, then the empty string "" is returned.</p>
    387      *
    388      * @return String The innermost diagnostic context.
    389      */
    390     public static String pop() {
    391         return contextStack.pop();
    392     }
    393
    394     /**
    395      * Looks at the last diagnostic context at the top of this NDC
    396      * without removing it.
    397      *
    398      * <p>The returned value is the value that was pushed last. If no
    399      * context is available, then the empty string "" is returned.</p>
    400      *
    401      * @return String The innermost diagnostic context.
    402      */
    403     public static String peek() {
    404         return contextStack.peek();
    405     }
    406
    407     /**
    408      * Pushes new diagnostic context information for the current thread.
    409      *
    410      * <p>The contents of the <code>message</code> parameter is
    411      * determined solely by the client.</p>
    412      *
    413      * @param message The new diagnostic context information.
    414      */
    415     public static void push(final String message) {
    416         contextStack.push(message);
    417     }
    418     /**
    419      * Pushes new diagnostic context information for the current thread.
    420      *
    421      * <p>The contents of the <code>message</code> and args parameters are
    422      * determined solely by the client. The message will be treated as a format String
    423      * and tokens will be replaced with the String value of the arguments in accordance
    424      * with ParameterizedMessage.</p>
    425      *
    426      * @param message The new diagnostic context information.
    427      * @param args Parameters for the message.
    428      */
    429     public static void push(final String message, final Object... args) {
    430         contextStack.push(ParameterizedMessage.format(message, args));
    431     }
    432
    433     /**
    434      * Removes the diagnostic context for this thread.
    435      *
    436      * <p>Each thread that created a diagnostic context by calling
    437      * {@link #push} should call this method before exiting. Otherwise,
    438      * the memory used by the <b>thread</b> cannot be reclaimed by the
    439      * VM.</p>
    440      *
    441      * <p>As this is such an important problem in heavy duty systems and
    442      * because it is difficult to always guarantee that the remove
    443      * method is called before exiting a thread, this method has been
    444      * augmented to lazily remove references to dead threads. In
    445      * practice, this means that you can be a little sloppy and
    446      * occasionally forget to call {@link #remove} before exiting a
    447      * thread. However, you must call <code>remove</code> sometime. If
    448      * you never call it, then your application is sure to run out of
    449      * memory.</p>
    450      */
    451     public static void removeStack() {
    452         contextStack.clear();
    453     }
    454
    455     /**
    456      * Trims elements from this diagnostic context. If the current
    457      * depth is smaller or equal to <code>maxDepth</code>, then no
    458      * action is taken. If the current depth is larger than newDepth
    459      * then all elements at maxDepth or higher are discarded.
    460      *
    461      * <p>This method is a convenient alternative to multiple {@link
    462      * #pop} calls. Moreover, it is often the case that at the end of
    463      * complex call sequences, the depth of the ThreadContext is
    464      * unpredictable. The <code>trim</code> method circumvents
    465      * this problem.</p>
    466      *
    467      * <p>For example, the combination</p>
    468      * <pre>
    469      * void foo() {
    470      *     final int depth = ThreadContext.getDepth();
    471      *
    472      *     // ... complex sequence of calls
    473      *
    474      *     ThreadContext.trim(depth);
    475      * }
    476      * </pre>
    477      *
    478      * <p>ensures that between the entry and exit of {@code foo} the depth of the
    479      * diagnostic stack is conserved.</p>
    480      *
    481      * @see #getDepth
    482      * @param depth The number of elements to keep.
    483      */
    484     public static void trim(final int depth) {
    485         contextStack.trim(depth);
    486     }
    487
    488     /**
    489      * The ThreadContext Stack interface.
    490      */
    491     public interface ContextStack extends Serializable, Collection<String> {
    492
    493         /**
    494          * Returns the element at the top of the stack.
    495          * @return The element at the top of the stack.
    496          * @throws java.util.NoSuchElementException if the stack is empty.
    497          */
    498         String pop();
    499
    500         /**
    501          * Returns the element at the top of the stack without removing it or null if the stack is empty.
    502          * @return the element at the top of the stack or null if the stack is empty.
    503          */
    504         String peek();
    505
    506         /**
    507          * Pushes an element onto the stack.
    508          * @param message The element to add.
    509          */
    510         void push(String message);
    511
    512         /**
    513          * Returns the number of elements in the stack.
    514          * @return the number of elements in the stack.
    515          */
    516         int getDepth();
    517
    518         /**
    519          * Returns all the elements in the stack in a List.
    520          * @return all the elements in the stack in a List.
    521          */
    522         List<String> asList();
    523
    524         /**
    525          * Trims elements from the end of the stack.
    526          * @param depth The maximum number of items in the stack to keep.
    527          */
    528         void trim(int depth);
    529
    530         /**
    531          * Returns a copy of the ContextStack.
    532          * @return a copy of the ContextStack.
    533          */
    534         ContextStack copy();
    535
    536         /**
    537          * Returns a ContextStack with the same contents as this ContextStack or {@code null}.
    538          * Attempts to modify the returned stack may or may not throw an exception, but will not affect the contents
    539          * of this ContextStack.
    540          * @return a ContextStack with the same contents as this ContextStack or {@code null}.
    541          */
    542         ContextStack getImmutableStackOrNull();
    543     }
    544 }