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 /**
89 * As of SLF4J version 1.7.14, StaticMDCBinder classes shipping in various bindings
90 * come with a getSingleton() method. Previously only a public field called SINGLETON
91 * was available.
92 *
93 * @return MDCAdapter
94 * @throws NoClassDefFoundError in case no binding is available
95 * @since 1.7.14
96 */
97 private static MDCAdapter bwCompatibleGetMDCAdapterFromBinder() throws NoClassDefFoundError {
98 try {
99 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 }