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 018 package org.apache.logging.log4j; 019 020 import java.io.Serializable; 021 import java.util.ArrayList; 022 import java.util.Collection; 023 import java.util.Collections; 024 import java.util.Iterator; 025 import java.util.List; 026 import java.util.Map; 027 028 import org.apache.logging.log4j.message.ParameterizedMessage; 029 import org.apache.logging.log4j.spi.DefaultThreadContextMap; 030 import org.apache.logging.log4j.spi.DefaultThreadContextStack; 031 import org.apache.logging.log4j.spi.LoggerContextFactory; 032 import org.apache.logging.log4j.spi.MutableThreadContextStack; 033 import org.apache.logging.log4j.spi.Provider; 034 import org.apache.logging.log4j.spi.ThreadContextMap; 035 import org.apache.logging.log4j.spi.ThreadContextStack; 036 import org.apache.logging.log4j.status.StatusLogger; 037 import org.apache.logging.log4j.util.PropertiesUtil; 038 import org.apache.logging.log4j.util.ProviderUtil; 039 040 /** 041 * The ThreadContext allows applications to store information either in a Map or a Stack. 042 * <p> 043 * <b><em>The MDC is managed on a per thread basis</em></b>. A child thread automatically inherits a <em>copy</em> of 044 * the mapped diagnostic context of its parent. 045 * </p> 046 */ 047 public final class ThreadContext { 048 049 /** 050 * Empty, immutable Map. 051 */ 052 public static final Map<String, String> EMPTY_MAP = Collections.emptyMap(); 053 054 /** 055 * Empty, immutable ContextStack. 056 */ 057 public static final ThreadContextStack EMPTY_STACK = new MutableThreadContextStack(new ArrayList<String>()); 058 059 private static final String DISABLE_MAP = "disableThreadContextMap"; 060 private static final String DISABLE_STACK = "disableThreadContextStack"; 061 private static final String DISABLE_ALL = "disableThreadContext"; 062 private static final String THREAD_CONTEXT_KEY = "log4j2.threadContextMap"; 063 064 private static boolean all; 065 private static boolean useMap; 066 private static boolean useStack; 067 private static ThreadContextMap contextMap; 068 private static ThreadContextStack contextStack; 069 private static final Logger LOGGER = StatusLogger.getLogger(); 070 071 static { 072 final PropertiesUtil managerProps = PropertiesUtil.getProperties(); 073 all = managerProps.getBooleanProperty(DISABLE_ALL); 074 useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || all); 075 contextStack = new DefaultThreadContextStack(useStack); 076 077 useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || all); 078 String threadContextMapName = managerProps.getStringProperty(THREAD_CONTEXT_KEY); 079 final ClassLoader cl = ProviderUtil.findClassLoader(); 080 if (threadContextMapName != null) { 081 try { 082 final Class<?> clazz = cl.loadClass(threadContextMapName); 083 if (ThreadContextMap.class.isAssignableFrom(clazz)) { 084 contextMap = (ThreadContextMap) clazz.newInstance(); 085 } 086 } catch (final ClassNotFoundException cnfe) { 087 LOGGER.error("Unable to locate configured LoggerContextFactory {}", threadContextMapName); 088 } catch (final Exception ex) { 089 LOGGER.error("Unable to create configured LoggerContextFactory {}", threadContextMapName, ex); 090 } 091 } 092 if (contextMap == null && ProviderUtil.hasProviders()) { 093 final LoggerContextFactory factory = LogManager.getFactory(); 094 final Iterator<Provider> providers = ProviderUtil.getProviders(); 095 while (providers.hasNext()) { 096 final Provider provider = providers.next(); 097 threadContextMapName = provider.getThreadContextMap(); 098 final String factoryClassName = provider.getClassName(); 099 if (threadContextMapName != null && factory.getClass().getName().equals(factoryClassName)) { 100 try { 101 final Class<?> clazz = cl.loadClass(threadContextMapName); 102 if (ThreadContextMap.class.isAssignableFrom(clazz)) { 103 contextMap = (ThreadContextMap) clazz.newInstance(); 104 break; 105 } 106 } catch (final ClassNotFoundException cnfe) { 107 LOGGER.error("Unable to locate configured LoggerContextFactory {}", threadContextMapName); 108 contextMap = new DefaultThreadContextMap(useMap); 109 } catch (final Exception ex) { 110 LOGGER.error("Unable to create configured LoggerContextFactory {}", threadContextMapName, ex); 111 contextMap = new DefaultThreadContextMap(useMap); 112 } 113 } 114 } 115 } 116 if (contextMap == null) { 117 contextMap = new DefaultThreadContextMap(useMap); 118 } 119 } 120 121 private ThreadContext() { 122 123 } 124 125 /** 126 * Put a context value (the <code>value</code> parameter) as identified 127 * with the <code>key</code> parameter into the current thread's 128 * context map. 129 * <p/> 130 * <p>If the current thread does not have a context map it is 131 * created as a side effect. 132 * @param key The key name. 133 * @param value The key value. 134 */ 135 public static void put(final String key, final String value) { 136 contextMap.put(key, value); 137 } 138 139 /** 140 * Get the context value identified by the <code>key</code> parameter. 141 * <p/> 142 * <p>This method has no side effects. 143 * @param key The key to locate. 144 * @return The value associated with the key or null. 145 */ 146 public static String get(final String key) { 147 return contextMap.get(key); 148 } 149 150 /** 151 * Remove the context value identified by the <code>key</code> parameter. 152 * @param key The key to remove. 153 */ 154 public static void remove(final String key) { 155 contextMap.remove(key); 156 } 157 158 /** 159 * Clear the context. 160 */ 161 public static void clear() { 162 contextMap.clear(); 163 } 164 165 /** 166 * Determine if the key is in the context. 167 * @param key The key to locate. 168 * @return True if the key is in the context, false otherwise. 169 */ 170 public static boolean containsKey(final String key) { 171 return contextMap.containsKey(key); 172 } 173 174 /** 175 * Returns a mutable copy of current thread's context Map. 176 * @return a mutable copy of the context. 177 */ 178 public static Map<String, String> getContext() { 179 return contextMap.getCopy(); 180 } 181 182 /** 183 * Returns an immutable view of the current thread's context Map. 184 * @return An immutable view of the ThreadContext Map. 185 */ 186 public static Map<String, String> getImmutableContext() { 187 final Map<String, String> map = contextMap.getImmutableMapOrNull(); 188 return map == null ? EMPTY_MAP : map; 189 } 190 191 /** 192 * Returns true if the Map is empty. 193 * @return true if the Map is empty, false otherwise. 194 */ 195 public static boolean isEmpty() { 196 return contextMap.isEmpty(); 197 } 198 199 /** 200 * Clear the stack for this thread. 201 */ 202 public static void clearStack() { 203 contextStack.clear(); 204 } 205 206 /** 207 * Returns a copy of this thread's stack. 208 * @return A copy of this thread's stack. 209 */ 210 public static ContextStack cloneStack() { 211 return contextStack.copy(); 212 } 213 214 /** 215 * Get an immutable copy of this current thread's context stack. 216 * @return an immutable copy of the ThreadContext stack. 217 */ 218 public static ContextStack getImmutableStack() { 219 return contextStack; 220 } 221 222 /** 223 * Set this thread's stack. 224 * @param stack The stack to use. 225 */ 226 public static void setStack(final Collection<String> stack) { 227 if (stack.size() == 0 || !useStack) { 228 return; 229 } 230 contextStack.clear(); 231 contextStack.addAll(stack); 232 } 233 234 /** 235 * Get the current nesting depth of this thread's stack. 236 * @return the number of items in the stack. 237 * 238 * @see #trim 239 */ 240 public static int getDepth() { 241 return contextStack.getDepth(); 242 } 243 244 /** 245 * Returns the value of the last item placed on the stack. 246 * <p/> 247 * <p>The returned value is the value that was pushed last. If no 248 * context is available, then the empty string "" is returned. 249 * 250 * @return String The innermost diagnostic context. 251 */ 252 public static String pop() { 253 return contextStack.pop(); 254 } 255 256 /** 257 * Looks at the last diagnostic context at the top of this NDC 258 * without removing it. 259 * <p/> 260 * <p>The returned value is the value that was pushed last. If no 261 * context is available, then the empty string "" is returned. 262 * 263 * @return String The innermost diagnostic context. 264 */ 265 public static String peek() { 266 return contextStack.peek(); 267 } 268 269 /** 270 * Push new diagnostic context information for the current thread. 271 * <p/> 272 * <p>The contents of the <code>message</code> parameter is 273 * determined solely by the client. 274 * 275 * @param message The new diagnostic context information. 276 */ 277 public static void push(final String message) { 278 contextStack.push(message); 279 } 280 /** 281 * Push new diagnostic context information for the current thread. 282 * <p/> 283 * <p>The contents of the <code>message</code> and args parameters are 284 * determined solely by the client. The message will be treated as a format String 285 * and tokens will be replaced with the String value of the arguments in accordance 286 * with ParameterizedMessage. 287 * 288 * @param message The new diagnostic context information. 289 * @param args Parameters for the message. 290 */ 291 public static void push(final String message, final Object... args) { 292 contextStack.push(ParameterizedMessage.format(message, args)); 293 } 294 295 /** 296 * Remove the diagnostic context for this thread. 297 * <p/> 298 * <p>Each thread that created a diagnostic context by calling 299 * {@link #push} should call this method before exiting. Otherwise, 300 * the memory used by the <b>thread</b> cannot be reclaimed by the 301 * VM. 302 * <p/> 303 * <p>As this is such an important problem in heavy duty systems and 304 * because it is difficult to always guarantee that the remove 305 * method is called before exiting a thread, this method has been 306 * augmented to lazily remove references to dead threads. In 307 * practice, this means that you can be a little sloppy and 308 * occasionally forget to call {@link #remove} before exiting a 309 * thread. However, you must call <code>remove</code> sometime. If 310 * you never call it, then your application is sure to run out of 311 * memory. 312 */ 313 public static void removeStack() { 314 contextStack.clear(); 315 } 316 317 /** 318 * Trims elements from this diagnostic context. If the current 319 * depth is smaller or equal to <code>maxDepth</code>, then no 320 * action is taken. If the current depth is larger than newDepth 321 * then all elements at maxDepth or higher are discarded. 322 * <p/> 323 * <p>This method is a convenient alternative to multiple {@link 324 * #pop} calls. Moreover, it is often the case that at the end of 325 * complex call sequences, the depth of the ThreadContext is 326 * unpredictable. The <code>trim</code> method circumvents 327 * this problem. 328 * <p/> 329 * <p>For example, the combination 330 * <pre> 331 * void foo() { 332 * int depth = ThreadContext.getDepth(); 333 * <p/> 334 * ... complex sequence of calls 335 * <p/> 336 * ThreadContext.trim(depth); 337 * } 338 * </pre> 339 * <p/> 340 * ensures that between the entry and exit of foo the depth of the 341 * diagnostic stack is conserved. 342 * 343 * @see #getDepth 344 * @param depth The number of elements to keep. 345 */ 346 public static void trim(final int depth) { 347 contextStack.trim(depth); 348 } 349 350 /** 351 * The ThreadContext Stack interface. 352 */ 353 public interface ContextStack extends Serializable { 354 355 /** 356 * Clears all elements from the stack. 357 */ 358 void clear(); 359 360 /** 361 * Returns the element at the top of the stack. 362 * @return The element at the top of the stack. 363 * @throws java.util.NoSuchElementException if the stack is empty. 364 */ 365 String pop(); 366 367 /** 368 * Returns the element at the top of the stack without removing it or null if the stack is empty. 369 * @return the element at the top of the stack or null if the stack is empty. 370 */ 371 String peek(); 372 373 /** 374 * Add an element to the stack. 375 * @param message The element to add. 376 */ 377 void push(String message); 378 379 /** 380 * Returns the number of elements in the stack. 381 * @return the number of elements in the stack. 382 */ 383 int getDepth(); 384 385 /** 386 * Returns all the elements in the stack in a List. 387 * @return all the elements in the stack in a List. 388 */ 389 List<String> asList(); 390 391 /** 392 * Trims elements from the end of the stack. 393 * @param depth The maximum number of items in the stack to keep. 394 */ 395 void trim(int depth); 396 397 /** 398 * Returns a copy of the ContextStack. 399 * @return a copy of the ContextStack.s 400 */ 401 ContextStack copy(); 402 } 403 }