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.Deque; 029import java.util.Map; 030 031import org.slf4j.helpers.*; 032import org.slf4j.spi.MDCAdapter; 033import org.slf4j.spi.SLF4JServiceProvider; 034 035/** 036 * This class hides and serves as a substitute for the underlying logging 037 * system's MDC implementation. 038 * 039 * <p> 040 * If the underlying logging system offers MDC functionality, then SLF4J's MDC, 041 * i.e. this class, will delegate to the underlying system's MDC. Note that at 042 * this time, only two logging systems, namely log4j and logback, offer MDC 043 * functionality. For java.util.logging which does not support MDC, 044 * {@link BasicMDCAdapter} will be used. For other systems, i.e. slf4j-simple 045 * and slf4j-nop, {@link NOPMDCAdapter} will be used. 046 * 047 * <p> 048 * Thus, as a SLF4J user, you can take advantage of MDC in the presence of log4j, 049 * logback, or java.util.logging, but without forcing these systems as 050 * dependencies upon your users. 051 * 052 * <p> 053 * For more information on MDC please see the <a 054 * href="http://logback.qos.ch/manual/mdc.html">chapter on MDC</a> in the 055 * logback manual. 056 * 057 * <p> 058 * Please note that all methods in this class are static. 059 * 060 * @author Ceki Gülcü 061 * @since 1.4.1 062 */ 063public class MDC { 064 065 static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA"; 066 private static final String MDC_APAPTER_CANNOT_BE_NULL_MESSAGE = "MDCAdapter cannot be null. See also " + NULL_MDCA_URL; 067 static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder"; 068 static MDCAdapter MDC_ADAPTER; 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 private static MDCAdapter getMDCAdapterGivenByProvider() { 089 SLF4JServiceProvider provider = LoggerFactory.getProvider(); 090 if(provider != null) { 091 // If you wish to change the mdc adapter, setting the MDC.MDCAdapter variable might be insufficient. 092 // Keep in mind that the provider *might* perform additional internal mdcAdapter assignments that 093 // you would also need to replicate/adapt. 094 095 // obtain and attach the MDCAdapter from the provider 096 097 final MDCAdapter anAdapter = provider.getMDCAdapter(); 098 emitTemporaryMDCAdapterWarningIfNeeded(provider); 099 return anAdapter; 100 } else { 101 Reporter.error("Failed to find provider."); 102 Reporter.error("Defaulting to no-operation MDCAdapter implementation."); 103 return new NOPMDCAdapter(); 104 } 105 } 106 107 private static void emitTemporaryMDCAdapterWarningIfNeeded(SLF4JServiceProvider provider) { 108 boolean isSubstitute = provider instanceof SubstituteServiceProvider; 109 if(isSubstitute) { 110 Reporter.info("Temporary mdcAdapter given by SubstituteServiceProvider."); 111 Reporter.info("This mdcAdapter will be replaced after backend initialization has completed."); 112 } 113 } 114 115 /** 116 * Put a diagnostic context value (the <code>val</code> parameter) as identified with the 117 * <code>key</code> parameter into the current thread's diagnostic context map. The 118 * <code>key</code> parameter cannot be null. The <code>val</code> parameter 119 * can be null only if the underlying implementation supports it. 120 * 121 * <p> 122 * This method delegates all work to the MDC of the underlying logging system. 123 * 124 * @param key non-null key 125 * @param val value to put in the map 126 * 127 * @throws IllegalArgumentException 128 * in case the "key" parameter is null 129 */ 130 public static void put(String key, String val) throws IllegalArgumentException { 131 if (key == null) { 132 throw new IllegalArgumentException("key parameter cannot be null"); 133 } 134 if (getMDCAdapter() == null) { 135 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 136 } 137 getMDCAdapter().put(key, val); 138 } 139 140 /** 141 * Put a diagnostic context value (the <code>val</code> parameter) as identified with the 142 * <code>key</code> parameter into the current thread's diagnostic context map. The 143 * <code>key</code> parameter cannot be null. The <code>val</code> parameter 144 * can be null only if the underlying implementation supports it. 145 * 146 * <p> 147 * This method delegates all work to the MDC of the underlying logging system. 148 * <p> 149 * This method return a <code>Closeable</code> object who can remove <code>key</code> when 150 * <code>close</code> is called. 151 * 152 * <p> 153 * Useful with Java 7 for example : 154 * <code> 155 * try(MDC.MDCCloseable closeable = MDC.putCloseable(key, value)) { 156 * .... 157 * } 158 * </code> 159 * 160 * @param key non-null key 161 * @param val value to put in the map 162 * @return a <code>Closeable</code> who can remove <code>key</code> when <code>close</code> 163 * is called. 164 * 165 * @throws IllegalArgumentException 166 * in case the "key" parameter is null 167 */ 168 public static MDCCloseable putCloseable(String key, String val) throws IllegalArgumentException { 169 put(key, val); 170 return new MDCCloseable(key); 171 } 172 173 /** 174 * Get the diagnostic context identified by the <code>key</code> parameter. The 175 * <code>key</code> parameter cannot be null. 176 * 177 * <p> 178 * This method delegates all work to the MDC of the underlying logging system. 179 * 180 * @param key a key 181 * @return the string value identified by the <code>key</code> parameter. 182 * @throws IllegalArgumentException 183 * in case the "key" parameter is null 184 */ 185 public static String get(String key) throws IllegalArgumentException { 186 if (key == null) { 187 throw new IllegalArgumentException("key parameter cannot be null"); 188 } 189 190 if (getMDCAdapter() == null) { 191 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 192 } 193 return getMDCAdapter().get(key); 194 } 195 196 /** 197 * Remove the diagnostic context identified by the <code>key</code> parameter using 198 * the underlying system's MDC implementation. The <code>key</code> parameter 199 * cannot be null. This method does nothing if there is no previous value 200 * associated with <code>key</code>. 201 * 202 * @param key a key 203 * @throws IllegalArgumentException 204 * in case the "key" parameter is null 205 */ 206 public static void remove(String key) throws IllegalArgumentException { 207 if (key == null) { 208 throw new IllegalArgumentException("key parameter cannot be null"); 209 } 210 211 if (getMDCAdapter() == null) { 212 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 213 } 214 getMDCAdapter().remove(key); 215 } 216 217 /** 218 * Clear all entries in the MDC of the underlying implementation. 219 */ 220 public static void clear() { 221 if (getMDCAdapter() == null) { 222 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 223 } 224 getMDCAdapter().clear(); 225 } 226 227 /** 228 * Return a copy of the current thread's context map, with keys and values of 229 * type String. Returned value may be null. 230 * 231 * @return A copy of the current thread's context map. May be null. 232 * @since 1.5.1 233 */ 234 public static Map<String, String> getCopyOfContextMap() { 235 if (getMDCAdapter() == null) { 236 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 237 } 238 return getMDCAdapter().getCopyOfContextMap(); 239 } 240 241 /** 242 * Set the current thread's context map by first clearing any existing map and 243 * then copying the map passed as parameter. The context map passed as 244 * parameter must only contain keys and values of type String. 245 * 246 * Null valued argument is allowed (since SLF4J version 2.0.0). 247 * 248 * @param contextMap 249 * must contain only keys and values of type String 250 * @since 1.5.1 251 */ 252 public static void setContextMap(Map<String, String> contextMap) { 253 if (getMDCAdapter() == null) { 254 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 255 } 256 getMDCAdapter().setContextMap(contextMap); 257 } 258 259 /** 260 * Returns the MDCAdapter instance currently in use. 261 * 262 * Since 2.0.17, if the MDCAdapter instance is null, then this method set it to use 263 * the adapter returned by the SLF4JProvider. However, in the vast majority of cases 264 * the MDCAdapter will be set earlier (during initialization) by {@link LoggerFactory}. 265 * 266 * @return the MDcAdapter instance currently in use. 267 * @since 1.4.2 268 */ 269 public static MDCAdapter getMDCAdapter() { 270 if(MDC_ADAPTER == null) { 271 MDC_ADAPTER = getMDCAdapterGivenByProvider(); 272 } 273 return MDC_ADAPTER; 274 } 275 276 /** 277 * Set MDCAdapter instance to use. 278 * 279 * @since 2.0.17 280 */ 281 static void setMDCAdapter(MDCAdapter anMDCAdapter) { 282 if(anMDCAdapter == null) { 283 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 284 } 285 MDC_ADAPTER = anMDCAdapter; 286 } 287 288 /** 289 * Push a value into the deque(stack) referenced by 'key'. 290 * 291 * @param key identifies the appropriate stack 292 * @param value the value to push into the stack 293 * @since 2.0.0 294 */ 295 static public void pushByKey(String key, String value) { 296 if (getMDCAdapter() == null) { 297 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 298 } 299 getMDCAdapter().pushByKey(key, value); 300 } 301 302 /** 303 * Pop the stack referenced by 'key' and return the value possibly null. 304 * 305 * @param key identifies the deque(stack) 306 * @return the value just popped. May be null/ 307 * @since 2.0.0 308 */ 309 static public String popByKey(String key) { 310 if (getMDCAdapter() == null) { 311 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 312 } 313 return getMDCAdapter().popByKey(key); 314 } 315 316 /** 317 * Returns a copy of the deque(stack) referenced by 'key'. May be null. 318 * 319 * @param key identifies the stack 320 * @return copy of stack referenced by 'key'. May be null. 321 * 322 * @since 2.0.0 323 */ 324 public Deque<String> getCopyOfDequeByKey(String key) { 325 if (getMDCAdapter() == null) { 326 throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE); 327 } 328 return getMDCAdapter().getCopyOfDequeByKey(key); 329 } 330}