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.Map;
    29
    30  import org.slf4j.helpers.NOPMDCAdapter;
    31  import org.slf4j.helpers.BasicMDCAdapter;
    32  import org.slf4j.helpers.Util;
    33  import org.slf4j.impl.StaticMDCBinder;
    34  import org.slf4j.spi.MDCAdapter;
    35
    36  /**
    37   * This class hides and serves as a substitute for the underlying logging
    38   * system's MDC implementation.
    39   *
    40   * <p>
    41   * If the underlying logging system offers MDC functionality, then SLF4J's MDC,
    42   * i.e. this class, will delegate to the underlying system's MDC. Note that at
    43   * this time, only two logging systems, namely log4j and logback, offer MDC
    44   * functionality. For java.util.logging which does not support MDC,
    45   * {@link BasicMDCAdapter} will be used. For other systems, i.e. slf4j-simple
    46   * and slf4j-nop, {@link NOPMDCAdapter} will be used.
    47   *
    48   * <p>
    49   * Thus, as a SLF4J user, you can take advantage of MDC in the presence of log4j,
    50   * logback, or java.util.logging, but without forcing these systems as
    51   * dependencies upon your users.
    52   *
    53   * <p>
    54   * For more information on MDC please see the <a
    55   * href="http://logback.qos.ch/manual/mdc.html">chapter on MDC</a> in the
    56   * logback manual.
    57   *
    58   * <p>
    59   * Please note that all methods in this class are static.
    60   *
    61   * @author Ceki G&uuml;lc&uuml;
    62   * @since 1.4.1
    63   */
    64  public class MDC {
    65
    66      static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA";
    67      static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder";
    68      static MDCAdapter mdcAdapter;
    69
    70      /**
    71       * An adapter to remove the key when done.
    72       */
    73      public static class MDCCloseable implements Closeable {
    74          private final String key;
    75
    76          private MDCCloseable(String key) {
    77              this.key = key;
    78          }
    79
    80          public void close() {
    81              MDC.remove(this.key);
    82          }
    83      }
    84
    85      private MDC() {
    86      }
    87
    88      /**
    89       * As of SLF4J version 1.7.14, StaticMDCBinder classes shipping in various bindings
    90       * come with a getSingleton() method. Previously only a public field called SINGLETON
    91       * was available.
    92       *
    93       * @return MDCAdapter
    94       * @throws NoClassDefFoundError in case no binding is available
    95       * @since 1.7.14
    96       */
    97      private static MDCAdapter bwCompatibleGetMDCAdapterFromBinder() throws NoClassDefFoundError {
    98          try {
    99              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 }