001/**
002 * Copyright (c) 2004-2011 QOS.ch
003 * All rights reserved.
004 *
005 * Permission is hereby granted, free  of charge, to any person obtaining
006 * a  copy  of this  software  and  associated  documentation files  (the
007 * "Software"), to  deal in  the Software without  restriction, including
008 * without limitation  the rights to  use, copy, modify,  merge, publish,
009 * distribute,  sublicense, and/or sell  copies of  the Software,  and to
010 * permit persons to whom the Software  is furnished to do so, subject to
011 * the following conditions:
012 *
013 * The  above  copyright  notice  and  this permission  notice  shall  be
014 * included in all copies or substantial portions of the Software.
015 *
016 * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
017 * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
018 * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
020 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
021 * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
022 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
023 *
024 */
025package org.slf4j;
026
027import java.io.Closeable;
028import java.util.Deque;
029import java.util.Map;
030
031import org.slf4j.helpers.BasicMDCAdapter;
032import org.slf4j.helpers.NOPMDCAdapter;
033import org.slf4j.helpers.Reporter;
034import org.slf4j.helpers.Util;
035import org.slf4j.spi.MDCAdapter;
036import org.slf4j.spi.SLF4JServiceProvider;
037
038/**
039 * This class hides and serves as a substitute for the underlying logging
040 * system's MDC implementation.
041 * 
042 * <p>
043 * If the underlying logging system offers MDC functionality, then SLF4J's MDC,
044 * i.e. this class, will delegate to the underlying system's MDC. Note that at
045 * this time, only two logging systems, namely log4j and logback, offer MDC
046 * functionality. For java.util.logging which does not support MDC,
047 * {@link BasicMDCAdapter} will be used. For other systems, i.e. slf4j-simple
048 * and slf4j-nop, {@link NOPMDCAdapter} will be used.
049 *
050 * <p>
051 * Thus, as a SLF4J user, you can take advantage of MDC in the presence of log4j,
052 * logback, or java.util.logging, but without forcing these systems as
053 * dependencies upon your users.
054 * 
055 * <p>
056 * For more information on MDC please see the <a
057 * href="http://logback.qos.ch/manual/mdc.html">chapter on MDC</a> in the
058 * logback manual.
059 * 
060 * <p>
061 * Please note that all methods in this class are static.
062 * 
063 * @author Ceki G&uuml;lc&uuml;
064 * @since 1.4.1
065 */
066public class MDC {
067
068    static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA";
069    private static final String MDC_APAPTER_CANNOT_BE_NULL_MESSAGE = "MDCAdapter cannot be null. See also " + NULL_MDCA_URL;
070    static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder";
071    static MDCAdapter mdcAdapter;
072
073    /**
074     * An adapter to remove the key when done.
075     */
076    public static class MDCCloseable implements Closeable {
077        private final String key;
078
079        private MDCCloseable(String key) {
080            this.key = key;
081        }
082
083        public void close() {
084            MDC.remove(this.key);
085        }
086    }
087
088    private MDC() {
089    }
090
091    static {
092        SLF4JServiceProvider provider = LoggerFactory.getProvider();
093        if (provider != null) {
094            mdcAdapter = provider.getMDCAdapter();
095        } else {
096            Reporter.error("Failed to find provider.");
097            Reporter.error("Defaulting to no-operation MDCAdapter implementation.");
098            mdcAdapter = new NOPMDCAdapter();
099        }
100    }
101
102    /**
103     * Put a diagnostic context value (the <code>val</code> parameter) as identified with the
104     * <code>key</code> parameter into the current thread's diagnostic context map. The
105     * <code>key</code> parameter cannot be null. The <code>val</code> parameter
106     * can be null only if the underlying implementation supports it.
107     * 
108     * <p>
109     * This method delegates all work to the MDC of the underlying logging system.
110     *
111     * @param key non-null key 
112     * @param val value to put in the map
113     * 
114     * @throws IllegalArgumentException
115     *           in case the "key" parameter is null
116     */
117    public static void put(String key, String val) throws IllegalArgumentException {
118        if (key == null) {
119            throw new IllegalArgumentException("key parameter cannot be null");
120        }
121        if (mdcAdapter == null) {
122            throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
123        }
124        mdcAdapter.put(key, val);
125    }
126
127    /**
128     * Put a diagnostic context value (the <code>val</code> parameter) as identified with the
129     * <code>key</code> parameter into the current thread's diagnostic context map. The
130     * <code>key</code> parameter cannot be null. The <code>val</code> parameter
131     * can be null only if the underlying implementation supports it.
132     *
133     * <p>
134     * This method delegates all work to the MDC of the underlying logging system.
135     * <p>
136     * This method return a <code>Closeable</code> object who can remove <code>key</code> when
137     * <code>close</code> is called.
138     *
139     * <p>
140     * Useful with Java 7 for example :
141     * <code>
142     *   try(MDC.MDCCloseable closeable = MDC.putCloseable(key, value)) {
143     *     ....
144     *   }
145     * </code>
146     *
147     * @param key non-null key
148     * @param val value to put in the map
149     * @return a <code>Closeable</code> who can remove <code>key</code> when <code>close</code>
150     * is called.
151     *
152     * @throws IllegalArgumentException
153     *           in case the "key" parameter is null
154     */
155    public static MDCCloseable putCloseable(String key, String val) throws IllegalArgumentException {
156        put(key, val);
157        return new MDCCloseable(key);
158    }
159
160    /**
161     * Get the diagnostic context identified by the <code>key</code> parameter. The
162     * <code>key</code> parameter cannot be null.
163     * 
164     * <p>
165     * This method delegates all work to the MDC of the underlying logging system.
166     *
167     * @param key a key
168     * @return the string value identified by the <code>key</code> parameter.
169     * @throws IllegalArgumentException
170     *           in case the "key" parameter is null
171     */
172    public static String get(String key) throws IllegalArgumentException {
173        if (key == null) {
174            throw new IllegalArgumentException("key parameter cannot be null");
175        }
176
177        if (mdcAdapter == null) {
178            throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
179        }
180        return mdcAdapter.get(key);
181    }
182
183    /**
184     * Remove the diagnostic context identified by the <code>key</code> parameter using
185     * the underlying system's MDC implementation. The <code>key</code> parameter
186     * cannot be null. This method does nothing if there is no previous value
187     * associated with <code>key</code>.
188     *
189     * @param key  a key
190     * @throws IllegalArgumentException
191     *           in case the "key" parameter is null
192     */
193    public static void remove(String key) throws IllegalArgumentException {
194        if (key == null) {
195            throw new IllegalArgumentException("key parameter cannot be null");
196        }
197
198        if (mdcAdapter == null) {
199            throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
200        }
201        mdcAdapter.remove(key);
202    }
203
204    /**
205     * Clear all entries in the MDC of the underlying implementation.
206     */
207    public static void clear() {
208        if (mdcAdapter == null) {
209            throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
210        }
211        mdcAdapter.clear();
212    }
213
214    /**
215     * Return a copy of the current thread's context map, with keys and values of
216     * type String. Returned value may be null.
217     * 
218     * @return A copy of the current thread's context map. May be null.
219     * @since 1.5.1
220     */
221    public static Map<String, String> getCopyOfContextMap() {
222        if (mdcAdapter == null) {
223            throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
224        }
225        return mdcAdapter.getCopyOfContextMap();
226    }
227
228    /**
229     * Set the current thread's context map by first clearing any existing map and
230     * then copying the map passed as parameter. The context map passed as
231     * parameter must only contain keys and values of type String.
232     * 
233     * Null valued argument is allowed (since SLF4J version 2.0.0).
234     * 
235     * @param contextMap
236     *          must contain only keys and values of type String
237     * @since 1.5.1
238     */
239    public static void setContextMap(Map<String, String> contextMap) {
240        if (mdcAdapter == null) {
241            throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
242        }
243        mdcAdapter.setContextMap(contextMap);
244    }
245
246    /**
247     * Returns the MDCAdapter instance currently in use.
248     * 
249     * @return the MDcAdapter instance currently in use.
250     * @since 1.4.2
251     */
252    public static MDCAdapter getMDCAdapter() {
253        return mdcAdapter;
254    }
255
256
257
258    /**
259     * Push a value into the deque(stack) referenced by 'key'.
260     *      
261     * @param key identifies the appropriate stack
262     * @param value the value to push into the stack
263     * @since 2.0.0
264     */
265    static public void pushByKey(String key, String value) {
266        if (mdcAdapter == null) {
267            throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
268        }
269        mdcAdapter.pushByKey(key, value);
270    }
271    
272    /**
273     * Pop the stack referenced by 'key' and return the value possibly null.
274     * 
275     * @param key identifies the deque(stack)
276     * @return the value just popped. May be null/
277     * @since 2.0.0
278     */
279    static public String popByKey(String key) {
280        if (mdcAdapter == null) {
281            throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
282        }
283        return mdcAdapter.popByKey(key);
284    }
285
286    /**
287     * Returns a copy of the deque(stack) referenced by 'key'. May be null.
288     * 
289     * @param key identifies the  stack
290     * @return copy of stack referenced by 'key'. May be null.
291     * 
292     * @since 2.0.0
293     */
294    public Deque<String>  getCopyOfDequeByKey(String key) {
295        if (mdcAdapter == null) {
296            throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
297        }
298        return mdcAdapter.getCopyOfDequeByKey(key);
299    }
300}