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 org.apache.logging.log4j.message.ParameterizedMessage;
021    
022    import java.io.Serializable;
023    import java.util.ArrayList;
024    import java.util.Collection;
025    import java.util.HashMap;
026    import java.util.List;
027    import java.util.Map;
028    import java.util.NoSuchElementException;
029    
030    /**
031     * The ThreadContext allows applications to store information either in a Map
032     * <p/>
033     * <p><b><em>The MDC is managed on a per thread basis</em></b>. A
034     * child thread automatically inherits a <em>copy</em> of the mapped
035     * diagnostic context of its parent.
036     */
037    public final class ThreadContext {
038    
039        /**
040         * Empty, immutable Map.
041         */
042        public static final Map<String, String> EMPTY_MAP = new ImmutableMap();
043    
044        /**
045         * Empty, immutable ContextStack.
046         */
047        public static final ContextStack EMPTY_STACK = new ImmutableStack();
048    
049        private static final String DISABLE_MAP = "disableThreadContextMap";
050    
051        private static final String DISABLE_STACK = "disableThreadContextStack";
052    
053        private static final String DISABLE_ALL = "disableThreadContext";
054    
055        private static boolean all = Boolean.getBoolean(DISABLE_ALL);
056    
057        private static boolean useMap = !(Boolean.getBoolean(DISABLE_MAP) || all);
058    
059        private static boolean useStack = !(Boolean.getBoolean(DISABLE_STACK) || all);
060    
061        private static ThreadLocal<Map<String, String>> localMap =
062            new InheritableThreadLocal<Map<String, String>>() {
063                @Override
064                protected Map<String, String> childValue(Map<String, String> parentValue) {
065                    return parentValue == null || !useMap ? null : new HashMap<String, String>(parentValue);
066                }
067            };
068    
069        private static ThreadLocal<ContextStack> localStack = new ThreadLocal<ContextStack>();
070    
071        private ThreadContext() {
072    
073        }
074    
075        /**
076         * Put a context value (the <code>o</code> parameter) as identified
077         * with the <code>key</code> parameter into the current thread's
078         * context map.
079         * <p/>
080         * <p>If the current thread does not have a context map it is
081         * created as a side effect.
082         * @param key The key name.
083         * @param value The key value.
084         */
085        public static void put(String key, String value) {
086            if (!useMap) {
087                return;
088            }
089            Map<String, String> map = localMap.get();
090            if (map == null) {
091                map = new HashMap<String, String>();
092                localMap.set(map);
093            }
094            map.put(key, value);
095        }
096    
097        /**
098         * Get the context identified by the <code>key</code> parameter.
099         * <p/>
100         * <p>This method has no side effects.
101         * @param key The key to locate.
102         * @return The value associated with the key or null.
103         */
104        public static String get(String key) {
105            Map<String, String> map = localMap.get();
106            return map == null ? null : map.get(key);
107        }
108    
109        /**
110         * Remove the the context identified by the <code>key</code>
111         * parameter.
112         * @param key The key to remove.
113         */
114        public static void remove(String key) {
115            Map<String, String> map = localMap.get();
116            if (map != null) {
117                map.remove(key);
118            }
119        }
120    
121        /**
122         * Clear the context.
123         */
124        public static void clear() {
125            localMap.remove();
126        }
127    
128        /**
129         * Determine if the key is in the context.
130         * @param key The key to locate.
131         * @return True if the key is in the context, false otherwise.
132         */
133        public static boolean containsKey(String key) {
134            Map<String, String> map = localMap.get();
135            return map == null ? false : map.containsKey(key);
136        }
137    
138        /**
139         * Get a copy of current thread's context Map.
140         * @return a copy of the context.
141         */
142        public static Map<String, String> getContext() {
143            Map<String, String> map = localMap.get();
144            return map == null ? new HashMap<String, String>() : new HashMap<String, String>(map);
145        }
146    
147        /**
148         * Get an immutable copy of the current thread's context Map.
149         * @return An immutable copy of the ThreadContext Map.
150         */
151        public static Map<String, String> getImmutableContext() {
152            Map<String, String> map = localMap.get();
153            return map == null ? new ImmutableMap() : new ImmutableMap(map);
154        }
155    
156        /**
157         * Returns true if the Map is empty.
158         * @return true if the Map is empty, false otherwise.
159         */
160        public static boolean isEmpty() {
161            Map<String, String> map = localMap.get();
162            return map == null || map.size() == 0;
163        }
164    
165        /**
166         * Clear the stack for this thread.
167         */
168        public static void clearStack() {
169            localStack.remove();
170        }
171    
172        /**
173         * Returns a copy of this thread's stack.
174         * @return A copy of this thread's stack.
175         */
176        public static ContextStack cloneStack() {
177            ContextStack stack = localStack.get();
178            return stack == null ? new ThreadContextStack() : new ThreadContextStack(stack.asList());
179        }
180    
181        /**
182         * Get an immutable copy of this current thread's context stack.
183         * @return an immutable copy of the ThreadContext stack.
184         */
185        public static ContextStack getImmutableStack() {
186            ContextStack stack = localStack.get();
187            return stack == null ? EMPTY_STACK : new ImmutableStack(stack.asList());
188        }
189    
190        /**
191         * Set this thread's stack.
192         * @param stack The stack to use.
193         */
194        public static void setStack(Collection<String> stack) {
195            if (stack.size() == 0 || !useStack) {
196                return;
197            }
198            localStack.set(new ThreadContextStack(stack));
199        }
200    
201        /**
202         * Get the current nesting depth of this thread's stack.
203         * @return the number of items in the stack.
204         *
205         * @see #trim
206         */
207        public static int getDepth() {
208            ContextStack stack = localStack.get();
209            return stack == null ? 0 : stack.getDepth();
210        }
211    
212        /**
213         * Returns the value of the last item placed on the stack.
214         * <p/>
215         * <p>The returned value is the value that was pushed last. If no
216         * context is available, then the empty string "" is returned.
217         *
218         * @return String The innermost diagnostic context.
219         */
220        public static String pop() {
221            ContextStack s = localStack.get();
222            if (s == null || s.getDepth() == 0) {
223                return "";
224            }
225            return s.pop();
226        }
227    
228        /**
229         * Looks at the last diagnostic context at the top of this NDC
230         * without removing it.
231         * <p/>
232         * <p>The returned value is the value that was pushed last. If no
233         * context is available, then the empty string "" is returned.
234         *
235         * @return String The innermost diagnostic context.
236         */
237        public static String peek() {
238            ContextStack s = localStack.get();
239            if (s == null || s.getDepth() == 0) {
240                return "";
241            }
242            return s.peek();
243        }
244    
245        /**
246         * Push new diagnostic context information for the current thread.
247         * <p/>
248         * <p>The contents of the <code>message</code> parameter is
249         * determined solely by the client.
250         *
251         * @param message The new diagnostic context information.
252         */
253        public static void push(String message) {
254            if (!useStack) {
255                return;
256            }
257            ContextStack stack = localStack.get();
258            if (stack == null) {
259                stack = new ThreadContextStack();
260                localStack.set(stack);
261            }
262            stack.push(message);
263        }
264        /**
265         * Push new diagnostic context information for the current thread.
266         * <p/>
267         * <p>The contents of the <code>message</code> and args parameters are
268         * determined solely by the client. The message will be treated as a format String
269         * and tokens will be replaced with the String value of the arguments in accordance
270         * with ParameterizedMessage.
271         *
272         * @param message The new diagnostic context information.
273         */
274        public static void push(String message, Object... args) {
275            if (!useStack) {
276                return;
277            }
278            ContextStack stack = localStack.get();
279            if (stack == null) {
280                stack = new ThreadContextStack();
281                localStack.set(stack);
282            }
283            stack.push(ParameterizedMessage.format(message, args));
284        }
285    
286        /**
287         * Remove the diagnostic context for this thread.
288         * <p/>
289         * <p>Each thread that created a diagnostic context by calling
290         * {@link #push} should call this method before exiting. Otherwise,
291         * the memory used by the <b>thread</b> cannot be reclaimed by the
292         * VM.
293         * <p/>
294         * <p>As this is such an important problem in heavy duty systems and
295         * because it is difficult to always guarantee that the remove
296         * method is called before exiting a thread, this method has been
297         * augmented to lazily remove references to dead threads. In
298         * practice, this means that you can be a little sloppy and
299         * occasionally forget to call {@link #remove} before exiting a
300         * thread. However, you must call <code>remove</code> sometime. If
301         * you never call it, then your application is sure to run out of
302         * memory.
303         */
304        public static void removeStack() {
305            localStack.remove();
306        }
307    
308        /**
309         * Trims elements from this diagnostic context. If the current
310         * depth is smaller or equal to <code>maxDepth</code>, then no
311         * action is taken. If the current depth is larger than newDepth
312         * then all elements at maxDepth or higher are discarded.
313         * <p/>
314         * <p>This method is a convenient alternative to multiple {@link
315         * #pop} calls. Moreover, it is often the case that at the end of
316         * complex call sequences, the depth of the ThreadContext is
317         * unpredictable. The <code>trim</code> method circumvents
318         * this problem.
319         * <p/>
320         * <p>For example, the combination
321         * <pre>
322         * void foo() {
323         * &nbsp;  int depth = ThreadContext.getDepth();
324         * <p/>
325         * &nbsp;  ... complex sequence of calls
326         * <p/>
327         * &nbsp;  ThreadContext.trim(depth);
328         * }
329         * </pre>
330         * <p/>
331         * ensures that between the entry and exit of foo the depth of the
332         * diagnostic stack is conserved.
333         *
334         * @see #getDepth
335         * @param depth The number of elements to keep.
336         */
337        public static void trim(int depth) {
338            ContextStack stack = localStack.get();
339            if (stack != null) {
340                stack.trim(depth);
341            }
342        }
343    
344        /**
345         * The ThreadContext Stack interface.
346         */
347        public interface ContextStack extends Serializable {
348    
349            /**
350             * Clears all elements from the stack.
351             */
352            void clear();
353    
354            /**
355             * Returns the element at the top of the stack.
356             * @return The element at the top of the stack.
357             * @throws java.util.NoSuchElementException if the stack is empty.
358             */
359            String pop();
360    
361            /**
362             * Returns the element at the top of the stack without removing it or null if the stack is empty.
363             * @return the element at the top of the stack or null if the stack is empty.
364             */
365            String peek();
366    
367            /**
368             * Add an element to the stack.
369             * @param message The element to add.
370             */
371            void push(String message);
372    
373            /**
374             * Returns the number of elements in the stack.
375             * @return the number of elements in the stack.
376             */
377            int getDepth();
378    
379            /**
380             * Returns all the elements in the stack in a List.
381             * @return all the elements in the stack in a List.
382             */
383            List<String> asList();
384    
385            /**
386             * Trims elements from the end of the stack.
387             * @param depth The maximum number of items in the stack to keep.
388             */
389            void trim(int depth);
390    
391            /**
392             * Returns a copy of the ContextStack.
393             * @return a copy of the ContextStack.
394             */
395            ContextStack copy();
396        }
397    
398        /**
399         * The ContextStack implementation.
400         */
401        private static class ThreadContextStack extends ArrayList<String> implements ContextStack {
402    
403            private static final long serialVersionUID = 5050501L;
404    
405            public ThreadContextStack() {
406                super();
407            }
408    
409            public ThreadContextStack(Collection<String> collection) {
410                super(collection);
411            }
412    
413            public String pop() {
414                int index = size() - 1;
415                if (index >= 0) {
416                    String result = get(index);
417                    remove(index);
418                    return result;
419                }
420                throw new NoSuchElementException("The ThreadContext stack is empty");
421            }
422    
423            public String peek() {
424                int index = size() - 1;
425                if (index >= 0) {
426                    return get(index);
427                }
428                return null;
429            }
430    
431            public void push(String message) {
432                add(message);
433            }
434    
435            public int getDepth() {
436                return size();
437            }
438    
439            public List<String> asList() {
440                return this;
441            }
442    
443            public void trim(int depth) {
444                if (depth < 0) {
445                    throw new IllegalArgumentException("Maximum stack depth cannot be negative");
446                }
447                while (size() > depth) {
448                    remove(size() - 1);
449                }
450    
451            }
452    
453            public ContextStack copy() {
454                return new ThreadContextStack(this);
455            }
456        }
457    
458        /**
459         * An immutable ContextStack.
460         */
461        public static class ImmutableStack extends ThreadContextStack {
462    
463            private static final long serialVersionUID = 5050502L;
464    
465            public ImmutableStack() {
466            }
467    
468            public ImmutableStack(Collection<String> collection) {
469                super(collection);
470            }
471    
472            public ImmutableStack(ThreadContextStack stack) {
473                super(stack);
474            }
475    
476            @Override
477            public void push(String message) {
478                throw new UnsupportedOperationException("Stack cannot be modified");
479            }
480    
481            @Override
482            public void trim(int depth) {
483                throw new UnsupportedOperationException("Stack cannot be modified");
484            }
485        }
486    
487        /**
488         * An immutable Context Map.
489         */
490        public static class ImmutableMap extends HashMap<String, String> {
491            private static final long serialVersionUID = 5050503L;
492    
493            public ImmutableMap() {
494                super();
495            }
496    
497            public ImmutableMap(Map<String, String> map) {
498                super(map);
499            }
500    
501            @Override
502            public String put(String s, String s1) {
503                throw new UnsupportedOperationException("Map cannot be modified");
504            }
505    
506            @Override
507            public void putAll(Map<? extends String, ? extends String> map) {
508                throw new UnsupportedOperationException("Map cannot be modified");
509            }
510    
511            @Override
512            public String remove(Object o) {
513                throw new UnsupportedOperationException("Map cannot be modified");
514            }
515    
516            @Override
517            public void clear() {
518                throw new UnsupportedOperationException("Map cannot be modified");
519            }
520        }
521    }