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 }