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