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.Deque;
29 import java.util.Map;
30
31 import org.slf4j.helpers.BasicMDCAdapter;
32 import org.slf4j.helpers.NOPMDCAdapter;
33 import org.slf4j.helpers.Util;
34 import org.slf4j.spi.MDCAdapter;
35 import org.slf4j.spi.SLF4JServiceProvider;
36
37 /**
38 * This class hides and serves as a substitute for the underlying logging
39 * system's MDC implementation.
40 *
41 * <p>
42 * If the underlying logging system offers MDC functionality, then SLF4J's MDC,
43 * i.e. this class, will delegate to the underlying system's MDC. Note that at
44 * this time, only two logging systems, namely log4j and logback, offer MDC
45 * functionality. For java.util.logging which does not support MDC,
46 * {@link BasicMDCAdapter} will be used. For other systems, i.e. slf4j-simple
47 * and slf4j-nop, {@link NOPMDCAdapter} will be used.
48 *
49 * <p>
50 * Thus, as a SLF4J user, you can take advantage of MDC in the presence of log4j,
51 * logback, or java.util.logging, but without forcing these systems as
52 * dependencies upon your users.
53 *
54 * <p>
55 * For more information on MDC please see the <a
56 * href="http://logback.qos.ch/manual/mdc.html">chapter on MDC</a> in the
57 * logback manual.
58 *
59 * <p>
60 * Please note that all methods in this class are static.
61 *
62 * @author Ceki Gülcü
63 * @since 1.4.1
64 */
65 public class MDC {
66
67 static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA";
68 private static final String MDC_APAPTER_CANNOT_BE_NULL_MESSAGE = "MDCAdapter cannot be null. See also " + NULL_MDCA_URL;
69 static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder";
70 static MDCAdapter mdcAdapter;
71
72 /**
73 * An adapter to remove the key when done.
74 */
75 public static class MDCCloseable implements Closeable {
76 private final String key;
77
78 private MDCCloseable(String key) {
79 this.key = key;
80 }
81
82 public void close() {
83 MDC.remove(this.key);
84 }
85 }
86
87 private MDC() {
88 }
89
90 static {
91 SLF4JServiceProvider provider = LoggerFactory.getProvider();
92 if (provider != null) {
93 mdcAdapter = provider.getMDCAdapter();
94 } else {
95 Util.report("Failed to find provider.");
96 Util.report("Defaulting to no-operation MDCAdapter implementation.");
97 mdcAdapter = new NOPMDCAdapter();
98 }
99 }
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 }