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_ADAPTER_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_ADAPTER_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 mdcAdapterNullCheck(); 191 return getMDCAdapter().get(key); 192 } 193 194 /** 195 * Remove the diagnostic context identified by the <code>key</code> parameter using 196 * the underlying system's MDC implementation. The <code>key</code> parameter 197 * cannot be null. This method does nothing if there is no previous value 198 * associated with <code>key</code>. 199 * 200 * @param key a key 201 * @throws IllegalArgumentException 202 * in case the "key" parameter is null 203 */ 204 public static void remove(String key) throws IllegalArgumentException { 205 if (key == null) { 206 throw new IllegalArgumentException("key parameter cannot be null"); 207 } 208 209 mdcAdapterNullCheck(); 210 getMDCAdapter().remove(key); 211 } 212 213 /** 214 * Clear all entries in the MDC of the underlying implementation. 215 */ 216 public static void clear() { 217 mdcAdapterNullCheck(); 218 getMDCAdapter().clear(); 219 } 220 221 /** 222 * Return a copy of the current thread's context map, with keys and values of 223 * type String. Returned value may be null. 224 * 225 * @return A copy of the current thread's context map. May be null. 226 * @since 1.5.1 227 */ 228 public static Map<String, String> getCopyOfContextMap() { 229 mdcAdapterNullCheck(); 230 return getMDCAdapter().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 * Null valued argument is allowed (since SLF4J version 2.0.0). 239 * 240 * @param contextMap 241 * must contain only keys and values of type String 242 * @since 1.5.1 243 */ 244 public static void setContextMap(Map<String, String> contextMap) { 245 mdcAdapterNullCheck(); 246 getMDCAdapter().setContextMap(contextMap); 247 } 248 249 /** 250 * Returns the MDCAdapter instance currently in use. 251 * 252 * Since 2.0.17, if the MDCAdapter instance is null, then this method set it to use 253 * the adapter returned by the SLF4JProvider. However, in the vast majority of cases 254 * the MDCAdapter will be set earlier (during initialization) by {@link LoggerFactory}. 255 * 256 * @return the MDcAdapter instance currently in use. 257 * @since 1.4.2 258 */ 259 public static MDCAdapter getMDCAdapter() { 260 if(MDC_ADAPTER == null) { 261 MDC_ADAPTER = getMDCAdapterGivenByProvider(); 262 } 263 return MDC_ADAPTER; 264 } 265 266 /** 267 * Set MDCAdapter instance to use. 268 * 269 * @since 2.0.17 270 */ 271 static void setMDCAdapter(MDCAdapter anMDCAdapter) { 272 if(anMDCAdapter == null) { 273 throw new IllegalStateException(MDC_ADAPTER_CANNOT_BE_NULL_MESSAGE); 274 } 275 MDC_ADAPTER = anMDCAdapter; 276 } 277 278 /** 279 * Push a value into the deque(stack) referenced by 'key'. 280 * 281 * @param key identifies the appropriate stack 282 * @param value the value to push into the stack 283 * @since 2.0.0 284 */ 285 static public void pushByKey(String key, String value) { 286 mdcAdapterNullCheck(); 287 getMDCAdapter().pushByKey(key, value); 288 } 289 290 /** 291 * Pop the <b>stack</b> referenced by 'key' and return the value possibly null. 292 * 293 * @param key identifies the deque(stack) 294 * @return the value just popped. May be null/ 295 * @since 2.0.0 296 */ 297 static public String popByKey(String key) { 298 mdcAdapterNullCheck(); 299 return getMDCAdapter().popByKey(key); 300 } 301 302 /** 303 * Returns a copy of the <b>deque(stack)</b> referenced by 'key'. May be null. 304 * 305 * @param key identifies the stack 306 * @return copy of stack referenced by 'key'. May be null. 307 * 308 * @since 2.0.0 309 */ 310 public Deque<String> getCopyOfDequeByKey(String key) { 311 mdcAdapterNullCheck(); 312 return getMDCAdapter().getCopyOfDequeByKey(key); 313 } 314 315 private static void mdcAdapterNullCheck() { 316 if (getMDCAdapter() == null) { 317 throw new IllegalStateException(MDC_ADAPTER_CANNOT_BE_NULL_MESSAGE); 318 } 319 } 320}