    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.Map;
    029
    030import org.slf4j.helpers.NOPMDCAdapter;
    031import org.slf4j.helpers.BasicMDCAdapter;
    032import org.slf4j.helpers.Util;
    033import org.slf4j.impl.StaticMDCBinder;
    034import org.slf4j.spi.MDCAdapter;
    035
    036/**
    037 * This class hides and serves as a substitute for the underlying logging
    038 * system's MDC implementation.
    039 *
    040 * <p>
    041 * If the underlying logging system offers MDC functionality, then SLF4J's MDC,
    042 * i.e. this class, will delegate to the underlying system's MDC. Note that at
    043 * this time, only two logging systems, namely log4j and logback, offer MDC
    044 * functionality. For java.util.logging which does not support MDC,
    045 * {@link BasicMDCAdapter} will be used. For other systems, i.e. slf4j-simple
    046 * and slf4j-nop, {@link NOPMDCAdapter} will be used.
    047 *
    048 * <p>
    049 * Thus, as a SLF4J user, you can take advantage of MDC in the presence of log4j,
    050 * logback, or java.util.logging, but without forcing these systems as
    051 * dependencies upon your users.
    052 *
    053 * <p>
    054 * For more information on MDC please see the <a
    055 * href="http://logback.qos.ch/manual/mdc.html">chapter on MDC</a> in the
    056 * logback manual.
    057 *
    058 * <p>
    059 * Please note that all methods in this class are static.
    060 *
    061 * @author Ceki G&uuml;lc&uuml;
    062 * @since 1.4.1
    063 */
    064public class MDC {
    065
    066    static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA";
    067    static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder";
    068    static MDCAdapter mdcAdapter;
    069
    070    /**
    071     * An adapter to remove the key when done.
    072     */
    073    public static class MDCCloseable implements Closeable {
    074        private final String key;
    075
    076        private MDCCloseable(String key) {
    077            this.key = key;
    078        }
    079
    080        public void close() {
    081            MDC.remove(this.key);
    082        }
    083    }
    084
    085    private MDC() {
    086    }
    087
    088    /**
    089     * As of SLF4J version 1.7.14, StaticMDCBinder classes shipping in various bindings
    090     * come with a getSingleton() method. Previously only a public field called SINGLETON
    091     * was available.
    092     *
    093     * @return MDCAdapter
    094     * @throws NoClassDefFoundError in case no binding is available
    095     * @since 1.7.14
    096     */
    097    private static MDCAdapter bwCompatibleGetMDCAdapterFromBinder() throws NoClassDefFoundError {
    098        try {
    099            return StaticMDCBinder.getSingleton().getMDCA();
    100        } catch (NoSuchMethodError nsme) {
    101            // binding is probably a version of SLF4J older than 1.7.14
    102            return StaticMDCBinder.SINGLETON.getMDCA();
    103        }
    104    }
    105
    106    static {
    107        try {
    108            mdcAdapter = bwCompatibleGetMDCAdapterFromBinder();
    109        } catch (NoClassDefFoundError ncde) {
    110            mdcAdapter = new NOPMDCAdapter();
    111            String msg = ncde.getMessage();
    112            if (msg != null && msg.contains("StaticMDCBinder")) {
    113                Util.report("Failed to load class \"org.slf4j.impl.StaticMDCBinder\".");
    114                Util.report("Defaulting to no-operation MDCAdapter implementation.");
    115                Util.report("See " + NO_STATIC_MDC_BINDER_URL + " for further details.");
    116            } else {
    117                throw ncde;
    118            }
    119        } catch (Exception e) {
    120            // we should never get here
    121            Util.report("MDC binding unsuccessful.", e);
    122        }
    123    }
    124
    125    /**
    126     * Put a diagnostic context value (the <code>val</code> parameter) as identified with the
    127     * <code>key</code> parameter into the current thread's diagnostic context map. The
    128     * <code>key</code> parameter cannot be null. The <code>val</code> parameter
    129     * can be null only if the underlying implementation supports it.
    130     *
    131     * <p>
    132     * This method delegates all work to the MDC of the underlying logging system.
    133     *
    134     * @param key non-null key
    135     * @param val value to put in the map
    136     *
    137     * @throws IllegalArgumentException
    138     *           in case the "key" parameter is null
    139     */
    140    public static void put(String key, String val) throws IllegalArgumentException {
    141        if (key == null) {
    142            throw new IllegalArgumentException("key parameter cannot be null");
    143        }
    144        if (mdcAdapter == null) {
    145            throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
    146        }
    147        mdcAdapter.put(key, val);
    148    }
    149
    150    /**
    151     * Put a diagnostic context value (the <code>val</code> parameter) as identified with the
    152     * <code>key</code> parameter into the current thread's diagnostic context map. The
    153     * <code>key</code> parameter cannot be null. The <code>val</code> parameter
    154     * can be null only if the underlying implementation supports it.
    155     *
    156     * <p>
    157     * This method delegates all work to the MDC of the underlying logging system.
    158     * <p>
    159     * This method return a <code>Closeable</code> object who can remove <code>key</code> when
    160     * <code>close</code> is called.
    161     *
    162     * <p>
    163     * Useful with Java 7 for example :
    164     * <code>
    165     *   try(MDC.MDCCloseable closeable = MDC.putCloseable(key, value)) {
    166     *     ....
    167     *   }
    168     * </code>
    169     *
    170     * @param key non-null key
    171     * @param val value to put in the map
    172     * @return a <code>Closeable</code> who can remove <code>key</code> when <code>close</code>
    173     * is called.
    174     *
    175     * @throws IllegalArgumentException
    176     *           in case the "key" parameter is null
    177     */
    178    public static MDCCloseable putCloseable(String key, String val) throws IllegalArgumentException {
    179        put(key, val);
    180        return new MDCCloseable(key);
    181    }
    182
    183    /**
    184     * Get the diagnostic context identified by the <code>key</code> parameter. The
    185     * <code>key</code> parameter cannot be null.
    186     *
    187     * <p>
    188     * This method delegates all work to the MDC of the underlying logging system.
    189     *
    190     * @param key
    191     * @return the string value identified by the <code>key</code> parameter.
    192     * @throws IllegalArgumentException
    193     *           in case the "key" parameter is null
    194     */
    195    public static String get(String key) throws IllegalArgumentException {
    196        if (key == null) {
    197            throw new IllegalArgumentException("key parameter cannot be null");
    198        }
    199
    200        if (mdcAdapter == null) {
    201            throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
    202        }
    203        return mdcAdapter.get(key);
    204    }
    205
    206    /**
    207     * Remove the diagnostic context identified by the <code>key</code> parameter using
    208     * the underlying system's MDC implementation. The <code>key</code> parameter
    209     * cannot be null. This method does nothing if there is no previous value
    210     * associated with <code>key</code>.
    211     *
    212     * @param key
    213     * @throws IllegalArgumentException
    214     *           in case the "key" parameter is null
    215     */
    216    public static void remove(String key) throws IllegalArgumentException {
    217        if (key == null) {
    218            throw new IllegalArgumentException("key parameter cannot be null");
    219        }
    220
    221        if (mdcAdapter == null) {
    222            throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
    223        }
    224        mdcAdapter.remove(key);
    225    }
    226
    227    /**
    228     * Clear all entries in the MDC of the underlying implementation.
    229     */
    230    public static void clear() {
    231        if (mdcAdapter == null) {
    232            throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
    233        }
    234        mdcAdapter.clear();
    235    }
    236
    237    /**
    238     * Return a copy of the current thread's context map, with keys and values of
    239     * type String. Returned value may be null.
    240     *
    241     * @return A copy of the current thread's context map. May be null.
    242     * @since 1.5.1
    243     */
    244    public static Map<String, String> getCopyOfContextMap() {
    245        if (mdcAdapter == null) {
    246            throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
    247        }
    248        return mdcAdapter.getCopyOfContextMap();
    249    }
    250
    251    /**
    252     * Set the current thread's context map by first clearing any existing map and
    253     * then copying the map passed as parameter. The context map passed as
    254     * parameter must only contain keys and values of type String.
    255     *
    256     * @param contextMap
    257     *          must contain only keys and values of type String
    258     * @since 1.5.1
    259     */
    260    public static void setContextMap(Map<String, String> contextMap) {
    261        if (mdcAdapter == null) {
    262            throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
    263        }
    264        mdcAdapter.setContextMap(contextMap);
    265    }
    266
    267    /**
    268     * Returns the MDCAdapter instance currently in use.
    269     *
    270     * @return the MDcAdapter instance currently in use.
    271     * @since 1.4.2
    272     */
    273    public static MDCAdapter getMDCAdapter() {
    274        return mdcAdapter;
    275    }
    276
    277}