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.Util; 034import org.slf4j.spi.MDCAdapter; 035import org.slf4j.spi.SLF4JServiceProvider; 036 037/** 038 * This class hides and serves as a substitute for the underlying logging 039 * system's MDC implementation. 040 * 041 * <p> 042 * If the underlying logging system offers MDC functionality, then SLF4J's MDC, 043 * i.e. this class, will delegate to the underlying system's MDC. Note that at 044 * this time, only two logging systems, namely log4j and logback, offer MDC 045 * functionality. For java.util.logging which does not support MDC, 046 * {@link BasicMDCAdapter} will be used. For other systems, i.e. slf4j-simple 047 * and slf4j-nop, {@link NOPMDCAdapter} will be used. 048 * 049 * <p> 050 * Thus, as a SLF4J user, you can take advantage of MDC in the presence of log4j, 051 * logback, or java.util.logging, but without forcing these systems as 052 * dependencies upon your users. 053 * 054 * <p> 055 * For more information on MDC please see the <a 056 * href="http://logback.qos.ch/manual/mdc.html">chapter on MDC</a> in the 057 * logback manual. 058 * 059 * <p> 060 * Please note that all methods in this class are static. 061 * 062 * @author Ceki Gülcü 063 * @since 1.4.1 064 */ 065public class MDC { 066 067 static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA"; 068 private static final String MDC_APAPTER_CANNOT_BE_NULL_MESSAGE = "MDCAdapter cannot be null. See also " + NULL_MDCA_URL; 069 static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder"; 070 static MDCAdapter mdcAdapter; 071 072 /** 073 * An adapter to remove the key when done. 074 */ 075 public static class MDCCloseable implements Closeable { 076 private final String key; 077 078 private MDCCloseable(String key) { 079 this.key = key; 080 } 081 082 public void close() { 083 MDC.remove(this.key); 084 } 085 } 086 087 private MDC() { 088 } 089 090 static { 091 SLF4JServiceProvider provider = LoggerFactory.getProvider(); 092 if (provider != null) { 093 mdcAdapter = provider.getMDCAdapter(); 094 } else { 095 Util.report("Failed to find provider."); 096 Util.report("Defaulting to no-operation MDCAdapter implementation."); 097 mdcAdapter = new NOPMDCAdapter(); 098 } 099 } 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}