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 MDCCloseable(String key)76 private MDCCloseable(String key) { 77 this.key = key; 78 } 79 close()80 public void close() { 81 MDC.remove(this.key); 82 } 83 } 84 MDC()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 */ bwCompatibleGetMDCAdapterFromBinder()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 */ put(String key, String val)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 */ putCloseable(String key, String val)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 */ get(String key)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 */ remove(String key)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 */ clear()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 */ getCopyOfContextMap()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 */ setContextMap(Map<String, String> contextMap)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 */ getMDCAdapter()273 public static MDCAdapter getMDCAdapter() { 274 return mdcAdapter; 275 } 276 277 } 278