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;
26  
27  import java.io.Closeable;
28  import java.util.Deque;
29  import java.util.Map;
30  
31  import org.slf4j.helpers.BasicMDCAdapter;
32  import org.slf4j.helpers.NOPMDCAdapter;
33  import org.slf4j.helpers.Util;
34  import org.slf4j.spi.MDCAdapter;
35  import org.slf4j.spi.SLF4JServiceProvider;
36  
37  /**
38   * This class hides and serves as a substitute for the underlying logging
39   * system's MDC implementation.
40   * 
41   * <p>
42   * If the underlying logging system offers MDC functionality, then SLF4J's MDC,
43   * i.e. this class, will delegate to the underlying system's MDC. Note that at
44   * this time, only two logging systems, namely log4j and logback, offer MDC
45   * functionality. For java.util.logging which does not support MDC,
46   * {@link BasicMDCAdapter} will be used. For other systems, i.e. slf4j-simple
47   * and slf4j-nop, {@link NOPMDCAdapter} will be used.
48   *
49   * <p>
50   * Thus, as a SLF4J user, you can take advantage of MDC in the presence of log4j,
51   * logback, or java.util.logging, but without forcing these systems as
52   * dependencies upon your users.
53   * 
54   * <p>
55   * For more information on MDC please see the <a
56   * href="http://logback.qos.ch/manual/mdc.html">chapter on MDC</a> in the
57   * logback manual.
58   * 
59   * <p>
60   * Please note that all methods in this class are static.
61   * 
62   * @author Ceki G&uuml;lc&uuml;
63   * @since 1.4.1
64   */
65  public class MDC {
66  
67      static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA";
68      private static final String MDC_APAPTER_CANNOT_BE_NULL_MESSAGE = "MDCAdapter cannot be null. See also " + NULL_MDCA_URL;
69      static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder";
70      static MDCAdapter mdcAdapter;
71  
72      /**
73       * An adapter to remove the key when done.
74       */
75      public static class MDCCloseable implements Closeable {
76          private final String key;
77  
78          private MDCCloseable(String key) {
79              this.key = key;
80          }
81  
82          public void close() {
83              MDC.remove(this.key);
84          }
85      }
86  
87      private MDC() {
88      }
89  
90      static {
91          SLF4JServiceProvider provider = LoggerFactory.getProvider();
92          if (provider != null) {
93              mdcAdapter = provider.getMDCAdapter();
94          } else {
95              Util.report("Failed to find provider.");
96              Util.report("Defaulting to no-operation MDCAdapter implementation.");
97              mdcAdapter = new NOPMDCAdapter();
98          }
99      }
100 
101     /**
102      * Put a diagnostic context value (the <code>val</code> parameter) as identified with the
103      * <code>key</code> parameter into the current thread's diagnostic context map. The
104      * <code>key</code> parameter cannot be null. The <code>val</code> parameter
105      * can be null only if the underlying implementation supports it.
106      * 
107      * <p>
108      * This method delegates all work to the MDC of the underlying logging system.
109      *
110      * @param key non-null key 
111      * @param val value to put in the map
112      * 
113      * @throws IllegalArgumentException
114      *           in case the "key" parameter is null
115      */
116     public static void put(String key, String val) throws IllegalArgumentException {
117         if (key == null) {
118             throw new IllegalArgumentException("key parameter cannot be null");
119         }
120         if (mdcAdapter == null) {
121             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
122         }
123         mdcAdapter.put(key, val);
124     }
125 
126     /**
127      * Put a diagnostic context value (the <code>val</code> parameter) as identified with the
128      * <code>key</code> parameter into the current thread's diagnostic context map. The
129      * <code>key</code> parameter cannot be null. The <code>val</code> parameter
130      * can be null only if the underlying implementation supports it.
131      *
132      * <p>
133      * This method delegates all work to the MDC of the underlying logging system.
134      * <p>
135      * This method return a <code>Closeable</code> object who can remove <code>key</code> when
136      * <code>close</code> is called.
137      *
138      * <p>
139      * Useful with Java 7 for example :
140      * <code>
141      *   try(MDC.MDCCloseable closeable = MDC.putCloseable(key, value)) {
142      *     ....
143      *   }
144      * </code>
145      *
146      * @param key non-null key
147      * @param val value to put in the map
148      * @return a <code>Closeable</code> who can remove <code>key</code> when <code>close</code>
149      * is called.
150      *
151      * @throws IllegalArgumentException
152      *           in case the "key" parameter is null
153      */
154     public static MDCCloseable putCloseable(String key, String val) throws IllegalArgumentException {
155         put(key, val);
156         return new MDCCloseable(key);
157     }
158 
159     /**
160      * Get the diagnostic context identified by the <code>key</code> parameter. The
161      * <code>key</code> parameter cannot be null.
162      * 
163      * <p>
164      * This method delegates all work to the MDC of the underlying logging system.
165      *
166      * @param key a key
167      * @return the string value identified by the <code>key</code> parameter.
168      * @throws IllegalArgumentException
169      *           in case the "key" parameter is null
170      */
171     public static String get(String key) throws IllegalArgumentException {
172         if (key == null) {
173             throw new IllegalArgumentException("key parameter cannot be null");
174         }
175 
176         if (mdcAdapter == null) {
177             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
178         }
179         return mdcAdapter.get(key);
180     }
181 
182     /**
183      * Remove the diagnostic context identified by the <code>key</code> parameter using
184      * the underlying system's MDC implementation. The <code>key</code> parameter
185      * cannot be null. This method does nothing if there is no previous value
186      * associated with <code>key</code>.
187      *
188      * @param key  a key
189      * @throws IllegalArgumentException
190      *           in case the "key" parameter is null
191      */
192     public static void remove(String key) throws IllegalArgumentException {
193         if (key == null) {
194             throw new IllegalArgumentException("key parameter cannot be null");
195         }
196 
197         if (mdcAdapter == null) {
198             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
199         }
200         mdcAdapter.remove(key);
201     }
202 
203     /**
204      * Clear all entries in the MDC of the underlying implementation.
205      */
206     public static void clear() {
207         if (mdcAdapter == null) {
208             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
209         }
210         mdcAdapter.clear();
211     }
212 
213     /**
214      * Return a copy of the current thread's context map, with keys and values of
215      * type String. Returned value may be null.
216      * 
217      * @return A copy of the current thread's context map. May be null.
218      * @since 1.5.1
219      */
220     public static Map<String, String> getCopyOfContextMap() {
221         if (mdcAdapter == null) {
222             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
223         }
224         return mdcAdapter.getCopyOfContextMap();
225     }
226 
227     /**
228      * Set the current thread's context map by first clearing any existing map and
229      * then copying the map passed as parameter. The context map passed as
230      * parameter must only contain keys and values of type String.
231      * 
232      * Null valued argument is allowed (since SLF4J version 2.0.0).
233      * 
234      * @param contextMap
235      *          must contain only keys and values of type String
236      * @since 1.5.1
237      */
238     public static void setContextMap(Map<String, String> contextMap) {
239         if (mdcAdapter == null) {
240             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
241         }
242         mdcAdapter.setContextMap(contextMap);
243     }
244 
245     /**
246      * Returns the MDCAdapter instance currently in use.
247      * 
248      * @return the MDcAdapter instance currently in use.
249      * @since 1.4.2
250      */
251     public static MDCAdapter getMDCAdapter() {
252         return mdcAdapter;
253     }
254 
255 
256 
257     /**
258      * Push a value into the deque(stack) referenced by 'key'.
259      *      
260      * @param key identifies the appropriate stack
261      * @param value the value to push into the stack
262      * @since 2.0.0
263      */
264     static public void pushByKey(String key, String value) {
265         if (mdcAdapter == null) {
266             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
267         }
268         mdcAdapter.pushByKey(key, value);
269     }
270     
271     /**
272      * Pop the stack referenced by 'key' and return the value possibly null.
273      * 
274      * @param key identifies the deque(stack)
275      * @return the value just popped. May be null/
276      * @since 2.0.0
277      */
278     static public String popByKey(String key) {
279         if (mdcAdapter == null) {
280             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
281         }
282         return mdcAdapter.popByKey(key);
283     }
284 
285     /**
286      * Returns a copy of the deque(stack) referenced by 'key'. May be null.
287      * 
288      * @param key identifies the  stack
289      * @return copy of stack referenced by 'key'. May be null.
290      * 
291      * @since 2.0.0
292      */
293     public Deque<String>  getCopyOfDequeByKey(String key) {
294         if (mdcAdapter == null) {
295             throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
296         }
297         return mdcAdapter.getCopyOfDequeByKey(key);
298     }
299 }