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