    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.AbstractCollection;
    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    import java.util.NoSuchElementException;
    028
    029    import org.apache.logging.log4j.message.ParameterizedMessage;
    030    import org.apache.logging.log4j.spi.DefaultThreadContextMap;
    031    import org.apache.logging.log4j.spi.DefaultThreadContextStack;
    032    import org.apache.logging.log4j.spi.Provider;
    033    import org.apache.logging.log4j.spi.ThreadContextMap;
    034    import org.apache.logging.log4j.spi.ThreadContextStack;
    035    import org.apache.logging.log4j.status.StatusLogger;
    036    import org.apache.logging.log4j.util.PropertiesUtil;
    037    import org.apache.logging.log4j.util.ProviderUtil;
    038
    039    /**
    040     * The ThreadContext allows applications to store information either in a Map or a Stack.
    041     * <p>
    042     * <b><em>The MDC is managed on a per thread basis</em></b>. A child thread automatically inherits a <em>copy</em> of
    043     * the mapped diagnostic context of its parent.
    044     * </p>
    045     */
    046    public final class ThreadContext  {
    047
    048        /**
    049         * An empty read-only ThreadContextStack.
    050         */
    051        private static class EmptyThreadContextStack extends AbstractCollection<String> implements ThreadContextStack {
    052
    053            private static final long serialVersionUID = 1L;
    054
    055            private static final Iterator<String> EMPTY_ITERATOR = new EmptyIterator<String>();
    056
    057            @Override
    058            public String pop() {
    059                return null;
    060            }
    061
    062            @Override
    063            public String peek() {
    064                return null;
    065            }
    066
    067            @Override
    068            public void push(final String message) {
    069                throw new UnsupportedOperationException();
    070            }
    071
    072            @Override
    073            public int getDepth() {
    074                return 0;
    075            }
    076
    077            @Override
    078            public List<String> asList() {
    079                return Collections.emptyList();
    080            }
    081
    082            @Override
    083            public void trim(final int depth) {
    084                // Do nothing
    085            }
    086
    087            @Override
    088            public boolean equals(final Object o) {
    089                // Similar to java.util.Collections.EmptyList.equals(Object)
    090                return (o instanceof Collection) && ((Collection<?>) o).isEmpty();
    091            }
    092
    093            @Override
    094            public int hashCode() {
    095                // Same as java.util.Collections.EmptyList.hashCode()
    096                return 1;
    097            }
    098
    099            @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    }