001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.log4j; 019 020import java.lang.reflect.InvocationTargetException; 021import java.lang.reflect.Method; 022import java.util.Hashtable; 023import org.apache.log4j.helpers.Loader; 024import org.apache.log4j.helpers.ThreadLocalMap; 025 026/** 027 The MDC class is similar to the {@link NDC} class except that it is 028 based on a map instead of a stack. It provides <em>mapped 029 diagnostic contexts</em>. A <em>Mapped Diagnostic Context</em>, or 030 MDC in short, is an instrument for distinguishing interleaved log 031 output from different sources. Log output is typically interleaved 032 when a server handles multiple clients near-simultaneously. 033 034 <p><b><em>The MDC is managed on a per thread basis</em></b>. A 035 child thread automatically inherits a <em>copy</em> of the mapped 036 diagnostic context of its parent. 037 038 <p>The MDC class requires JDK 1.2 or above. Under JDK 1.1 the MDC 039 will always return empty values but otherwise will not affect or 040 harm your application. 041 042 @since 1.2 043 044 @author Ceki Gülcü 045*/ 046public class MDC { 047 048 final static MDC mdc = new MDC(); 049 050 static final int HT_SIZE = 7; 051 052 boolean java1; 053 054 Object tlm; 055 056 private Method removeMethod; 057 058 private 059 MDC() { 060 java1 = Loader.isJava1(); 061 if(!java1) { 062 tlm = new ThreadLocalMap(); 063 } 064 065 try { 066 removeMethod = ThreadLocal.class.getMethod("remove", null); 067 } catch (NoSuchMethodException e) { 068 // don't do anything - java prior 1.5 069 } 070 } 071 072 /** 073 Put a context value (the <code>o</code> parameter) as identified 074 with the <code>key</code> parameter into the current thread's 075 context map. 076 077 <p>If the current thread does not have a context map it is 078 created as a side effect. 079 080 */ 081 static 082 public 083 void put(String key, Object o) { 084 if (mdc != null) { 085 mdc.put0(key, o); 086 } 087 } 088 089 /** 090 Get the context identified by the <code>key</code> parameter. 091 092 <p>This method has no side effects. 093 */ 094 static 095 public 096 Object get(String key) { 097 if (mdc != null) { 098 return mdc.get0(key); 099 } 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}