View Javadoc
1   /**
2    * Copyright (c) 2004-2011 QOS.ch
3    * All rights reserved.
4    *
5    * Permission is hereby granted, free  of charge, to any person obtaining
6    * a  copy  of this  software  and  associated  documentation files  (the
7    * "Software"), to  deal in  the Software without  restriction, including
8    * without limitation  the rights to  use, copy, modify,  merge, publish,
9    * distribute,  sublicense, and/or sell  copies of  the Software,  and to
10   * permit persons to whom the Software  is furnished to do so, subject to
11   * the following conditions:
12   *
13   * The  above  copyright  notice  and  this permission  notice  shall  be
14   * included in all copies or substantial portions of the Software.
15   *
16   * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
17   * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
18   * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
19   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20   * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21   * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
22   * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23   *
24   */
25  package org.slf4j.helpers;
26  
27  import org.slf4j.spi.MDCAdapter;
28  
29  import java.util.*;
30  
31  /**
32   * Basic MDC implementation, which can be used with logging systems that lack
33   * out-of-the-box MDC support.
34   *
35   * This code was initially inspired by  logback's LogbackMDCAdapter. However,
36   * LogbackMDCAdapter has evolved and is now considerably more sophisticated.
37   *
38   * @author Ceki Gulcu
39   * @author Maarten Bosteels
40   * @author Lukasz Cwik
41   * 
42   * @since 1.5.0
43   */
44  public class BasicMDCAdapter implements MDCAdapter {
45  
46      private final ThreadLocalMapOfStacks threadLocalMapOfDeques = new ThreadLocalMapOfStacks();
47  
48      private final InheritableThreadLocal<Map<String, String>> inheritableThreadLocalMap = new InheritableThreadLocal<Map<String, String>>() {
49          @Override
50          protected Map<String, String> childValue(Map<String, String> parentValue) {
51              if (parentValue == null) {
52                  return null;
53              }
54              return new HashMap<>(parentValue);
55          }
56      };
57  
58      /**
59       * Put a context value (the <code>val</code> parameter) as identified with
60       * the <code>key</code> parameter into the current thread's context map.
61       * Note that contrary to log4j, the <code>val</code> parameter can be null.
62       *
63       * <p>
64       * If the current thread does not have a context map it is created as a side
65       * effect of this call.
66       *
67       * @throws IllegalArgumentException
68       *                 in case the "key" parameter is null
69       */
70      public void put(String key, String val) {
71          if (key == null) {
72              throw new IllegalArgumentException("key cannot be null");
73          }
74          Map<String, String> map = inheritableThreadLocalMap.get();
75          if (map == null) {
76              map = new HashMap<>();
77              inheritableThreadLocalMap.set(map);
78          }
79          map.put(key, val);
80      }
81  
82      /**
83       * Get the context identified by the <code>key</code> parameter.
84       */
85      public String get(String key) {
86          Map<String, String> map = inheritableThreadLocalMap.get();
87          if ((map != null) && (key != null)) {
88              return map.get(key);
89          } else {
90              return null;
91          }
92      }
93  
94      /**
95       * Remove the the context identified by the <code>key</code> parameter.
96       */
97      public void remove(String key) {
98          Map<String, String> map = inheritableThreadLocalMap.get();
99          if (map != null) {
100             map.remove(key);
101         }
102     }
103 
104     /**
105      * Clear all entries in the MDC.
106      */
107     public void clear() {
108         Map<String, String> map = inheritableThreadLocalMap.get();
109         if (map != null) {
110             map.clear();
111             inheritableThreadLocalMap.remove();
112         }
113     }
114 
115     /**
116      * Returns the keys in the MDC as a {@link Set} of {@link String}s The
117      * returned value can be null.
118      *
119      * @return the keys in the MDC
120      */
121     public Set<String> getKeys() {
122         Map<String, String> map = inheritableThreadLocalMap.get();
123         if (map != null) {
124             return map.keySet();
125         } else {
126             return null;
127         }
128     }
129 
130     /**
131      * Return a copy of the current thread's context map.
132      * Returned value may be null.
133      *
134      */
135     public Map<String, String> getCopyOfContextMap() {
136         Map<String, String> oldMap = inheritableThreadLocalMap.get();
137         if (oldMap != null) {
138             return new HashMap<>(oldMap);
139         } else {
140             return null;
141         }
142     }
143 
144     public void setContextMap(Map<String, String> contextMap) {
145         Map<String, String> copy = null;
146         if (contextMap != null) {
147             copy = new HashMap<>(contextMap);
148         }
149         inheritableThreadLocalMap.set(copy);
150     }
151 
152     @Override
153     public void pushByKey(String key, String value) {
154         threadLocalMapOfDeques.pushByKey(key, value);
155     }
156 
157     @Override
158     public String popByKey(String key) {
159         return threadLocalMapOfDeques.popByKey(key);    
160      }
161 
162     @Override
163     public Deque<String> getCopyOfDequeByKey(String key) {
164         return threadLocalMapOfDeques.getCopyOfDequeByKey(key);
165     }
166     @Override
167     public void clearDequeByKey(String key) {
168         threadLocalMapOfDeques.clearDequeByKey(key);
169     }
170 }