1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.log4j;
19
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Method;
22 import java.util.Hashtable;
23 import org.apache.log4j.helpers.Loader;
24 import org.apache.log4j.helpers.ThreadLocalMap;
25
26 /**
27 The MDC class is similar to the {@link NDC} class except that it is
28 based on a map instead of a stack. It provides <em>mapped
29 diagnostic contexts</em>. A <em>Mapped Diagnostic Context</em>, or
30 MDC in short, is an instrument for distinguishing interleaved log
31 output from different sources. Log output is typically interleaved
32 when a server handles multiple clients near-simultaneously.
33
34 <p><b><em>The MDC is managed on a per thread basis</em></b>. A
35 child thread automatically inherits a <em>copy</em> of the mapped
36 diagnostic context of its parent.
37
38 <p>The MDC class requires JDK 1.2 or above. Under JDK 1.1 the MDC
39 will always return empty values but otherwise will not affect or
40 harm your application.
41
42 @since 1.2
43
44 @author Ceki Gülcü
45 */
46 public class MDC {
47
48 final static MDC mdc = new MDC();
49
50 static final int HT_SIZE = 7;
51
52 boolean java1;
53
54 Object tlm;
55
56 private Method removeMethod;
57
58 private
59 MDC() {
60 java1 = Loader.isJava1();
61 if(!java1) {
62 tlm = new ThreadLocalMap();
63 }
64
65 try {
66 removeMethod = ThreadLocal.class.getMethod("remove", null);
67 } catch (NoSuchMethodException e) {
68 // don't do anything - java prior 1.5
69 }
70 }
71
72 /**
73 Put a context value (the <code>o</code> parameter) as identified
74 with the <code>key</code> parameter into the current thread's
75 context map.
76
77 <p>If the current thread does not have a context map it is
78 created as a side effect.
79
80 */
81 static
82 public
83 void put(String key, Object o) {
84 if (mdc != null) {
85 mdc.put0(key, o);
86 }
87 }
88
89 /**
90 Get the context identified by the <code>key</code> parameter.
91
92 <p>This method has no side effects.
93 */
94 static
95 public
96 Object get(String key) {
97 if (mdc != null) {
98 return mdc.get0(key);
99 }
100 return null;
101 }
102
103 /**
104 Remove the the context identified by the <code>key</code>
105 parameter.
106
107 */
108 static
109 public
110 void remove(String key) {
111 if (mdc != null) {
112 mdc.remove0(key);
113 }
114 }
115
116
117 /**
118 * Get the current thread's MDC as a hashtable. This method is
119 * intended to be used internally.
120 * */
121 public static Hashtable getContext() {
122 if (mdc != null) {
123 return mdc.getContext0();
124 } else {
125 return null;
126 }
127 }
128
129 /**
130 * Remove all values from the MDC.
131 * @since 1.2.16
132 */
133 public static void clear() {
134 if (mdc != null) {
135 mdc.clear0();
136 }
137 }
138
139
140 private
141 void put0(String key, Object o) {
142 if(java1 || tlm == null) {
143 return;
144 } else {
145 Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();
146 if(ht == null) {
147 ht = new Hashtable(HT_SIZE);
148 ((ThreadLocalMap)tlm).set(ht);
149 }
150 ht.put(key, o);
151 }
152 }
153
154 private
155 Object get0(String key) {
156 if(java1 || tlm == null) {
157 return null;
158 } else {
159 Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();
160 if(ht != null && key != null) {
161 return ht.get(key);
162 } else {
163 return null;
164 }
165 }
166 }
167
168 private
169 void remove0(String key) {
170 if(!java1 && tlm != null) {
171 Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();
172 if(ht != null) {
173 ht.remove(key);
174 // clean up if this was the last key
175 if (ht.isEmpty()) {
176 clear0();
177 }
178 }
179 }
180 }
181
182
183 private
184 Hashtable getContext0() {
185 if(java1 || tlm == null) {
186 return null;
187 } else {
188 return (Hashtable) ((ThreadLocalMap)tlm).get();
189 }
190 }
191
192 private
193 void clear0() {
194 if(!java1 && tlm != null) {
195 Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();
196 if(ht != null) {
197 ht.clear();
198 }
199 if(removeMethod != null) {
200 // java 1.3/1.4 does not have remove - will suffer from a memory leak
201 try {
202 removeMethod.invoke(tlm, null);
203 } catch (IllegalAccessException e) {
204 // should not happen
205 } catch (InvocationTargetException e) {
206 // should not happen
207 }
208 }
209 }
210 }
211
212 }
213