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.Deque; 29 import java.util.Map; 30 31 import org.slf4j.helpers.BasicMDCAdapter; 32 import org.slf4j.helpers.NOPMDCAdapter; 33 import org.slf4j.helpers.Util; 34 import org.slf4j.spi.MDCAdapter; 35 import org.slf4j.spi.SLF4JServiceProvider; 36 37 /** 38 * This class hides and serves as a substitute for the underlying logging 39 * system's MDC implementation. 40 * 41 * <p> 42 * If the underlying logging system offers MDC functionality, then SLF4J's MDC, 43 * i.e. this class, will delegate to the underlying system's MDC. Note that at 44 * this time, only two logging systems, namely log4j and logback, offer MDC 45 * functionality. For java.util.logging which does not support MDC, 46 * {@link BasicMDCAdapter} will be used. For other systems, i.e. slf4j-simple 47 * and slf4j-nop, {@link NOPMDCAdapter} will be used. 48 * 49 * <p> 50 * Thus, as a SLF4J user, you can take advantage of MDC in the presence of log4j, 51 * logback, or java.util.logging, but without forcing these systems as 52 * dependencies upon your users. 53 * 54 * <p> 55 * For more information on MDC please see the <a 56 * href="http://logback.qos.ch/manual/mdc.html">chapter on MDC</a> in the 57 * logback manual. 58 * 59 * <p> 60 * Please note that all methods in this class are static. 61 * 62 * @author Ceki Gülcü 63 * @since 1.4.1 64 */ 65 public class MDC { 66 67 static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA"; 68 private static final String MDC_APAPTER_CANNOT_BE_NULL_MESSAGE = "MDCAdapter cannot be null. See also " + NULL_MDCA_URL; 69 static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder"; 70 static MDCAdapter mdcAdapter; 71 72 /** 73 * An adapter to remove the key when done. 74 */ 75 public static class MDCCloseable implements Closeable { 76 private final String key; 77 78 private MDCCloseable(String key) { 79 this.key = key; 80 } 81 82 public void close() { 83 MDC.remove(this.key); 84 } 85 } 86 87 private MDC() { 88 } 89 90 static { 91 SLF4JServiceProvider provider = LoggerFactory.getProvider(); 92 if (provider != null) { 93 mdcAdapter = provider.getMDCAdapter(); 94 } else { 95 Util.report("Failed to find provider."); 96 Util.report("Defaulting to no-operation MDCAdapter implementation."); 97 mdcAdapter = new NOPMDCAdapter(); 98 } 99 } 100 101 /** 102 * Put a diagnostic context value (the <code>val</code> parameter) as identified with the 103 * <code>key</code> parameter into the current thread's diagnostic context map. The 104 * <code>key</code> parameter cannot be null. The <code>val</code> parameter 105 * can be null only if the underlying implementation supports it. 106 * 107 * <p> 108 * This method delegates all work to the MDC of the underlying logging system. 109 * 110 * @param key non-null key 111 * @param val value to put in the map 112 * 113 * @throws IllegalArgumentException 114 * in case the "key" parameter is null 115 */ 116 public static void put(String key, String val) throws IllegalArgumentException { 117 if (key == null) { 118 throw new IllegalArgumentException("key parameter cannot be null"); 119 } 120 if (mdcAdapter == null) { 121 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 122 } 123 mdcAdapter.put(key, val); 124 } 125 126 /** 127 * Put a diagnostic context value (the <code>val</code> parameter) as identified with the 128 * <code>key</code> parameter into the current thread's diagnostic context map. The 129 * <code>key</code> parameter cannot be null. The <code>val</code> parameter 130 * can be null only if the underlying implementation supports it. 131 * 132 * <p> 133 * This method delegates all work to the MDC of the underlying logging system. 134 * <p> 135 * This method return a <code>Closeable</code> object who can remove <code>key</code> when 136 * <code>close</code> is called. 137 * 138 * <p> 139 * Useful with Java 7 for example : 140 * <code> 141 * try(MDC.MDCCloseable closeable = MDC.putCloseable(key, value)) { 142 * .... 143 * } 144 * </code> 145 * 146 * @param key non-null key 147 * @param val value to put in the map 148 * @return a <code>Closeable</code> who can remove <code>key</code> when <code>close</code> 149 * is called. 150 * 151 * @throws IllegalArgumentException 152 * in case the "key" parameter is null 153 */ 154 public static MDCCloseable putCloseable(String key, String val) throws IllegalArgumentException { 155 put(key, val); 156 return new MDCCloseable(key); 157 } 158 159 /** 160 * Get the diagnostic context identified by the <code>key</code> parameter. The 161 * <code>key</code> parameter cannot be null. 162 * 163 * <p> 164 * This method delegates all work to the MDC of the underlying logging system. 165 * 166 * @param key a key 167 * @return the string value identified by the <code>key</code> parameter. 168 * @throws IllegalArgumentException 169 * in case the "key" parameter is null 170 */ 171 public static String get(String key) throws IllegalArgumentException { 172 if (key == null) { 173 throw new IllegalArgumentException("key parameter cannot be null"); 174 } 175 176 if (mdcAdapter == null) { 177 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 178 } 179 return mdcAdapter.get(key); 180 } 181 182 /** 183 * Remove the diagnostic context identified by the <code>key</code> parameter using 184 * the underlying system's MDC implementation. The <code>key</code> parameter 185 * cannot be null. This method does nothing if there is no previous value 186 * associated with <code>key</code>. 187 * 188 * @param key a key 189 * @throws IllegalArgumentException 190 * in case the "key" parameter is null 191 */ 192 public static void remove(String key) throws IllegalArgumentException { 193 if (key == null) { 194 throw new IllegalArgumentException("key parameter cannot be null"); 195 } 196 197 if (mdcAdapter == null) { 198 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 199 } 200 mdcAdapter.remove(key); 201 } 202 203 /** 204 * Clear all entries in the MDC of the underlying implementation. 205 */ 206 public static void clear() { 207 if (mdcAdapter == null) { 208 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 209 } 210 mdcAdapter.clear(); 211 } 212 213 /** 214 * Return a copy of the current thread's context map, with keys and values of 215 * type String. Returned value may be null. 216 * 217 * @return A copy of the current thread's context map. May be null. 218 * @since 1.5.1 219 */ 220 public static Map<String, String> getCopyOfContextMap() { 221 if (mdcAdapter == null) { 222 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 223 } 224 return mdcAdapter.getCopyOfContextMap(); 225 } 226 227 /** 228 * Set the current thread's context map by first clearing any existing map and 229 * then copying the map passed as parameter. The context map passed as 230 * parameter must only contain keys and values of type String. 231 * 232 * Null valued argument is allowed (since SLF4J version 2.0.0). 233 * 234 * @param contextMap 235 * must contain only keys and values of type String 236 * @since 1.5.1 237 */ 238 public static void setContextMap(Map<String, String> contextMap) { 239 if (mdcAdapter == null) { 240 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 241 } 242 mdcAdapter.setContextMap(contextMap); 243 } 244 245 /** 246 * Returns the MDCAdapter instance currently in use. 247 * 248 * @return the MDcAdapter instance currently in use. 249 * @since 1.4.2 250 */ 251 public static MDCAdapter getMDCAdapter() { 252 return mdcAdapter; 253 } 254 255 256 257 /** 258 * Push a value into the deque(stack) referenced by 'key'. 259 * 260 * @param key identifies the appropriate stack 261 * @param value the value to push into the stack 262 * @since 2.0.0 263 */ 264 static public void pushByKey(String key, String value) { 265 if (mdcAdapter == null) { 266 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 267 } 268 mdcAdapter.pushByKey(key, value); 269 } 270 271 /** 272 * Pop the stack referenced by 'key' and return the value possibly null. 273 * 274 * @param key identifies the deque(stack) 275 * @return the value just popped. May be null/ 276 * @since 2.0.0 277 */ 278 static public String popByKey(String key) { 279 if (mdcAdapter == null) { 280 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 281 } 282 return mdcAdapter.popByKey(key); 283 } 284 285 /** 286 * Returns a copy of the deque(stack) referenced by 'key'. May be null. 287 * 288 * @param key identifies the stack 289 * @return copy of stack referenced by 'key'. May be null. 290 * 291 * @since 2.0.0 292 */ 293 public Deque<String> getCopyOfDequeByKey(String key) { 294 if (mdcAdapter == null) { 295 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 296 } 297 return mdcAdapter.getCopyOfDequeByKey(key); 298 } 299 }