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ülcü 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 static { 89 try { 90 mdcAdapter = StaticMDCBinder.SINGLETON.getMDCA(); 91 } catch (NoClassDefFoundError ncde) { 92 mdcAdapter = new NOPMDCAdapter(); 93 String msg = ncde.getMessage(); 94 if (msg != null && msg.indexOf("StaticMDCBinder") != -1) { 95 Util.report("Failed to load class \"org.slf4j.impl.StaticMDCBinder\"."); 96 Util.report("Defaulting to no-operation MDCAdapter implementation."); 97 Util.report("See " + NO_STATIC_MDC_BINDER_URL + " for further details."); 98 } else { 99 throw ncde; 100 } 101 } catch (Exception e) { 102 // we should never get here 103 Util.report("MDC binding unsuccessful.", e); 104 } 105 } 106 107 /** 108 * Put a diagnostic context value (the <code>val</code> parameter) as identified with the 109 * <code>key</code> parameter into the current thread's diagnostic context map. The 110 * <code>key</code> parameter cannot be null. The <code>val</code> parameter 111 * can be null only if the underlying implementation supports it. 112 * 113 * <p> 114 * This method delegates all work to the MDC of the underlying logging system. 115 * 116 * @param key non-null key 117 * @param val value to put in the map 118 * 119 * @throws IllegalArgumentException 120 * in case the "key" parameter is null 121 */ 122 public static void put(String key, String val) throws IllegalArgumentException { 123 if (key == null) { 124 throw new IllegalArgumentException("key parameter cannot be null"); 125 } 126 if (mdcAdapter == null) { 127 throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); 128 } 129 mdcAdapter.put(key, val); 130 } 131 132 /** 133 * Put a diagnostic context value (the <code>val</code> parameter) as identified with the 134 * <code>key</code> parameter into the current thread's diagnostic context map. The 135 * <code>key</code> parameter cannot be null. The <code>val</code> parameter 136 * can be null only if the underlying implementation supports it. 137 * 138 * <p> 139 * This method delegates all work to the MDC of the underlying logging system. 140 * <p> 141 * This method return a <code>Closeable</code> object who can remove <code>key</code> when 142 * <code>close</code> is called. 143 * 144 * <p> 145 * Useful with Java 7 for example : 146 * <code> 147 * try(MDC.MDCCloseable closeable = MDC.putCloseable(key, value)) { 148 * .... 149 * } 150 * </code> 151 * 152 * @param key non-null key 153 * @param val value to put in the map 154 * @return a <code>Closeable</code> who can remove <code>key</code> when <code>close</code> 155 * is called. 156 * 157 * @throws IllegalArgumentException 158 * in case the "key" parameter is null 159 */ 160 public static MDCCloseable putCloseable(String key, String val) throws IllegalArgumentException { 161 put(key, val); 162 return new MDCCloseable(key); 163 } 164 165 /** 166 * Get the diagnostic context identified by the <code>key</code> parameter. The 167 * <code>key</code> parameter cannot be null. 168 * 169 * <p> 170 * This method delegates all work to the MDC of the underlying logging system. 171 * 172 * @param key 173 * @return the string value identified by the <code>key</code> parameter. 174 * @throws IllegalArgumentException 175 * in case the "key" parameter is null 176 */ 177 public static String get(String key) throws IllegalArgumentException { 178 if (key == null) { 179 throw new IllegalArgumentException("key parameter cannot be null"); 180 } 181 182 if (mdcAdapter == null) { 183 throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); 184 } 185 return mdcAdapter.get(key); 186 } 187 188 /** 189 * Remove the diagnostic context identified by the <code>key</code> parameter using 190 * the underlying system's MDC implementation. The <code>key</code> parameter 191 * cannot be null. This method does nothing if there is no previous value 192 * associated with <code>key</code>. 193 * 194 * @param key 195 * @throws IllegalArgumentException 196 * in case the "key" parameter is null 197 */ 198 public static void remove(String key) throws IllegalArgumentException { 199 if (key == null) { 200 throw new IllegalArgumentException("key parameter cannot be null"); 201 } 202 203 if (mdcAdapter == null) { 204 throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); 205 } 206 mdcAdapter.remove(key); 207 } 208 209 /** 210 * Clear all entries in the MDC of the underlying implementation. 211 */ 212 public static void clear() { 213 if (mdcAdapter == null) { 214 throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); 215 } 216 mdcAdapter.clear(); 217 } 218 219 /** 220 * Return a copy of the current thread's context map, with keys and values of 221 * type String. Returned value may be null. 222 * 223 * @return A copy of the current thread's context map. May be null. 224 * @since 1.5.1 225 */ 226 public static Map<String, String> getCopyOfContextMap() { 227 if (mdcAdapter == null) { 228 throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); 229 } 230 return mdcAdapter.getCopyOfContextMap(); 231 } 232 233 /** 234 * Set the current thread's context map by first clearing any existing map and 235 * then copying the map passed as parameter. The context map passed as 236 * parameter must only contain keys and values of type String. 237 * 238 * @param contextMap 239 * must contain only keys and values of type String 240 * @since 1.5.1 241 */ 242 public static void setContextMap(Map<String, String> contextMap) { 243 if (mdcAdapter == null) { 244 throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); 245 } 246 mdcAdapter.setContextMap(contextMap); 247 } 248 249 /** 250 * Returns the MDCAdapter instance currently in use. 251 * 252 * @return the MDcAdapter instance currently in use. 253 * @since 1.4.2 254 */ 255 public static MDCAdapter getMDCAdapter() { 256 return mdcAdapter; 257 } 258 259 } 260