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.Map; 029 030import org.slf4j.helpers.NOPMDCAdapter; 031import org.slf4j.helpers.BasicMDCAdapter; 032import org.slf4j.helpers.Util; 033import org.slf4j.impl.StaticMDCBinder; 034import org.slf4j.spi.MDCAdapter; 035 036/** 037 * This class hides and serves as a substitute for the underlying logging 038 * system's MDC implementation. 039 * 040 * <p> 041 * If the underlying logging system offers MDC functionality, then SLF4J's MDC, 042 * i.e. this class, will delegate to the underlying system's MDC. Note that at 043 * this time, only two logging systems, namely log4j and logback, offer MDC 044 * functionality. For java.util.logging which does not support MDC, 045 * {@link BasicMDCAdapter} will be used. For other systems, i.e. slf4j-simple 046 * and slf4j-nop, {@link NOPMDCAdapter} will be used. 047 * 048 * <p> 049 * Thus, as a SLF4J user, you can take advantage of MDC in the presence of log4j, 050 * logback, or java.util.logging, but without forcing these systems as 051 * dependencies upon your users. 052 * 053 * <p> 054 * For more information on MDC please see the <a 055 * href="http://logback.qos.ch/manual/mdc.html">chapter on MDC</a> in the 056 * logback manual. 057 * 058 * <p> 059 * Please note that all methods in this class are static. 060 * 061 * @author Ceki Gülcü 062 * @since 1.4.1 063 */ 064public class MDC { 065 066 static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA"; 067 static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder"; 068 static MDCAdapter mdcAdapter; 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 /** 089 * As of SLF4J version 1.7.14, StaticMDCBinder classes shipping in various bindings 090 * come with a getSingleton() method. Previously only a public field called SINGLETON 091 * was available. 092 * 093 * @return MDCAdapter 094 * @throws NoClassDefFoundError in case no binding is available 095 * @since 1.7.14 096 */ 097 private static MDCAdapter bwCompatibleGetMDCAdapterFromBinder() throws NoClassDefFoundError { 098 try { 099 return StaticMDCBinder.getSingleton().getMDCA(); 100 } catch (NoSuchMethodError nsme) { 101 // binding is probably a version of SLF4J older than 1.7.14 102 return StaticMDCBinder.SINGLETON.getMDCA(); 103 } 104 } 105 106 static { 107 try { 108 mdcAdapter = bwCompatibleGetMDCAdapterFromBinder(); 109 } catch (NoClassDefFoundError ncde) { 110 mdcAdapter = new NOPMDCAdapter(); 111 String msg = ncde.getMessage(); 112 if (msg != null && msg.contains("StaticMDCBinder")) { 113 Util.report("Failed to load class \"org.slf4j.impl.StaticMDCBinder\"."); 114 Util.report("Defaulting to no-operation MDCAdapter implementation."); 115 Util.report("See " + NO_STATIC_MDC_BINDER_URL + " for further details."); 116 } else { 117 throw ncde; 118 } 119 } catch (Exception e) { 120 // we should never get here 121 Util.report("MDC binding unsuccessful.", e); 122 } 123 } 124 125 /** 126 * Put a diagnostic context value (the <code>val</code> parameter) as identified with the 127 * <code>key</code> parameter into the current thread's diagnostic context map. The 128 * <code>key</code> parameter cannot be null. The <code>val</code> parameter 129 * can be null only if the underlying implementation supports it. 130 * 131 * <p> 132 * This method delegates all work to the MDC of the underlying logging system. 133 * 134 * @param key non-null key 135 * @param val value to put in the map 136 * 137 * @throws IllegalArgumentException 138 * in case the "key" parameter is null 139 */ 140 public static void put(String key, String val) throws IllegalArgumentException { 141 if (key == null) { 142 throw new IllegalArgumentException("key parameter cannot be null"); 143 } 144 if (mdcAdapter == null) { 145 throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); 146 } 147 mdcAdapter.put(key, val); 148 } 149 150 /** 151 * Put a diagnostic context value (the <code>val</code> parameter) as identified with the 152 * <code>key</code> parameter into the current thread's diagnostic context map. The 153 * <code>key</code> parameter cannot be null. The <code>val</code> parameter 154 * can be null only if the underlying implementation supports it. 155 * 156 * <p> 157 * This method delegates all work to the MDC of the underlying logging system. 158 * <p> 159 * This method return a <code>Closeable</code> object who can remove <code>key</code> when 160 * <code>close</code> is called. 161 * 162 * <p> 163 * Useful with Java 7 for example : 164 * <code> 165 * try(MDC.MDCCloseable closeable = MDC.putCloseable(key, value)) { 166 * .... 167 * } 168 * </code> 169 * 170 * @param key non-null key 171 * @param val value to put in the map 172 * @return a <code>Closeable</code> who can remove <code>key</code> when <code>close</code> 173 * is called. 174 * 175 * @throws IllegalArgumentException 176 * in case the "key" parameter is null 177 */ 178 public static MDCCloseable putCloseable(String key, String val) throws IllegalArgumentException { 179 put(key, val); 180 return new MDCCloseable(key); 181 } 182 183 /** 184 * Get the diagnostic context identified by the <code>key</code> parameter. The 185 * <code>key</code> parameter cannot be null. 186 * 187 * <p> 188 * This method delegates all work to the MDC of the underlying logging system. 189 * 190 * @param key 191 * @return the string value identified by the <code>key</code> parameter. 192 * @throws IllegalArgumentException 193 * in case the "key" parameter is null 194 */ 195 public static String get(String key) throws IllegalArgumentException { 196 if (key == null) { 197 throw new IllegalArgumentException("key parameter cannot be null"); 198 } 199 200 if (mdcAdapter == null) { 201 throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); 202 } 203 return mdcAdapter.get(key); 204 } 205 206 /** 207 * Remove the diagnostic context identified by the <code>key</code> parameter using 208 * the underlying system's MDC implementation. The <code>key</code> parameter 209 * cannot be null. This method does nothing if there is no previous value 210 * associated with <code>key</code>. 211 * 212 * @param key 213 * @throws IllegalArgumentException 214 * in case the "key" parameter is null 215 */ 216 public static void remove(String key) throws IllegalArgumentException { 217 if (key == null) { 218 throw new IllegalArgumentException("key parameter cannot be null"); 219 } 220 221 if (mdcAdapter == null) { 222 throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); 223 } 224 mdcAdapter.remove(key); 225 } 226 227 /** 228 * Clear all entries in the MDC of the underlying implementation. 229 */ 230 public static void clear() { 231 if (mdcAdapter == null) { 232 throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); 233 } 234 mdcAdapter.clear(); 235 } 236 237 /** 238 * Return a copy of the current thread's context map, with keys and values of 239 * type String. Returned value may be null. 240 * 241 * @return A copy of the current thread's context map. May be null. 242 * @since 1.5.1 243 */ 244 public static Map<String, String> getCopyOfContextMap() { 245 if (mdcAdapter == null) { 246 throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); 247 } 248 return mdcAdapter.getCopyOfContextMap(); 249 } 250 251 /** 252 * Set the current thread's context map by first clearing any existing map and 253 * then copying the map passed as parameter. The context map passed as 254 * parameter must only contain keys and values of type String. 255 * 256 * @param contextMap 257 * must contain only keys and values of type String 258 * @since 1.5.1 259 */ 260 public static void setContextMap(Map<String, String> contextMap) { 261 if (mdcAdapter == null) { 262 throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL); 263 } 264 mdcAdapter.setContextMap(contextMap); 265 } 266 267 /** 268 * Returns the MDCAdapter instance currently in use. 269 * 270 * @return the MDcAdapter instance currently in use. 271 * @since 1.4.2 272 */ 273 public static MDCAdapter getMDCAdapter() { 274 return mdcAdapter; 275 } 276 277}