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.AbstractCollection; 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 import java.util.NoSuchElementException; 028 029 import org.apache.logging.log4j.message.ParameterizedMessage; 030 import org.apache.logging.log4j.spi.DefaultThreadContextMap; 031 import org.apache.logging.log4j.spi.DefaultThreadContextStack; 032 import org.apache.logging.log4j.spi.LoggerContextFactory; 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 * An empty read-only ThreadContextStack. 051 */ 052 private static class EmptyThreadContextStack extends AbstractCollection<String> implements ThreadContextStack { 053 054 private static final long serialVersionUID = 1L; 055 056 private static final Iterator<String> EMPTY_ITERATOR = new EmptyIterator<String>(); 057 058 @Override 059 public String pop() { 060 return null; 061 } 062 063 @Override 064 public String peek() { 065 return null; 066 } 067 068 @Override 069 public void push(final String message) { 070 throw new UnsupportedOperationException(); 071 } 072 073 @Override 074 public int getDepth() { 075 return 0; 076 } 077 078 @Override 079 public List<String> asList() { 080 return Collections.emptyList(); 081 } 082 083 @Override 084 public void trim(final int depth) { 085 // Do nothing 086 } 087 088 @Override 089 public boolean equals(final Object o) { 090 // Similar to java.util.Collections.EmptyList.equals(Object) 091 return (o instanceof Collection) && ((Collection<?>) o).isEmpty(); 092 } 093 094 @Override 095 public int hashCode() { 096 // Same as java.util.Collections.EmptyList.hashCode() 097 return 1; 098 } 099 100 @Override 101 public ContextStack copy() { 102 return this; 103 } 104 105 @Override 106 public <T> T[] toArray(final T[] a) { 107 throw new UnsupportedOperationException(); 108 } 109 110 @Override 111 public boolean add(final String e) { 112 throw new UnsupportedOperationException(); 113 } 114 115 @Override 116 public boolean containsAll(final Collection<?> c) { 117 return false; 118 } 119 120 @Override 121 public boolean addAll(final Collection<? extends String> c) { 122 throw new UnsupportedOperationException(); 123 } 124 125 @Override 126 public boolean removeAll(final Collection<?> c) { 127 throw new UnsupportedOperationException(); 128 } 129 130 @Override 131 public boolean retainAll(final Collection<?> c) { 132 throw new UnsupportedOperationException(); 133 } 134 135 @Override 136 public Iterator<String> iterator() { 137 return EMPTY_ITERATOR; 138 } 139 140 @Override 141 public int size() { 142 return 0; 143 } 144 145 @Override 146 public ContextStack getImmutableStackOrNull() { 147 return this; 148 } 149 } 150 151 /** 152 * An empty iterator. Since Java 1.7 added the Collections.emptyIterator() method, we have to make do. 153 * @param <E> the type of the empty iterator 154 */ 155 private static class EmptyIterator<E> implements Iterator<E> { 156 157 @Override 158 public boolean hasNext() { 159 return false; 160 } 161 162 @Override 163 public E next() { 164 throw new NoSuchElementException("This is an empty iterator!"); 165 } 166 167 @Override 168 public void remove() { 169 // no-op 170 } 171 } 172 173 /** 174 * Empty, immutable Map. 175 */ 176 // ironically, this annotation gives an "unsupported @SuppressWarnings" warning in Eclipse 177 @SuppressWarnings("PublicStaticCollectionField") // I like irony, so I won't delete it... 178 public static final Map<String, String> EMPTY_MAP = Collections.emptyMap(); 179 180 /** 181 * Empty, immutable ContextStack. 182 */ 183 // ironically, this annotation gives an "unsupported @SuppressWarnings" warning in Eclipse 184 @SuppressWarnings("PublicStaticCollectionField") 185 public static final ThreadContextStack EMPTY_STACK = new EmptyThreadContextStack(); 186 187 private static final String DISABLE_MAP = "disableThreadContextMap"; 188 private static final String DISABLE_STACK = "disableThreadContextStack"; 189 private static final String DISABLE_ALL = "disableThreadContext"; 190 private static final String THREAD_CONTEXT_KEY = "log4j2.threadContextMap"; 191 192 private static boolean disableAll; 193 private static boolean useMap; 194 private static boolean useStack; 195 private static ThreadContextMap contextMap; 196 private static ThreadContextStack contextStack; 197 private static final Logger LOGGER = StatusLogger.getLogger(); 198 199 static { 200 init(); 201 } 202 203 /** 204 * <em>Consider private, used for testing.</em> 205 */ 206 static void init() { 207 contextMap = null; 208 final PropertiesUtil managerProps = PropertiesUtil.getProperties(); 209 disableAll = managerProps.getBooleanProperty(DISABLE_ALL); 210 useStack = !(managerProps.getBooleanProperty(DISABLE_STACK) || disableAll); 211 useMap = !(managerProps.getBooleanProperty(DISABLE_MAP) || disableAll); 212 213 contextStack = new DefaultThreadContextStack(useStack); 214 String threadContextMapName = managerProps.getStringProperty(THREAD_CONTEXT_KEY); 215 final ClassLoader cl = ProviderUtil.findClassLoader(); 216 if (threadContextMapName != null) { 217 try { 218 final Class<?> clazz = cl.loadClass(threadContextMapName); 219 if (ThreadContextMap.class.isAssignableFrom(clazz)) { 220 contextMap = (ThreadContextMap) clazz.newInstance(); 221 } 222 } catch (final ClassNotFoundException cnfe) { 223 LOGGER.error("Unable to locate configured LoggerContextFactory {}", threadContextMapName); 224 } catch (final Exception ex) { 225 LOGGER.error("Unable to create configured LoggerContextFactory {}", threadContextMapName, ex); 226 } 227 } 228 if (contextMap == null && ProviderUtil.hasProviders()) { 229 final LoggerContextFactory factory = LogManager.getFactory(); 230 for (final Provider provider : ProviderUtil.getProviders()) { 231 threadContextMapName = provider.getThreadContextMap(); 232 final String factoryClassName = provider.getClassName(); 233 if (threadContextMapName != null && factory.getClass().getName().equals(factoryClassName)) { 234 try { 235 final Class<?> clazz = cl.loadClass(threadContextMapName); 236 if (ThreadContextMap.class.isAssignableFrom(clazz)) { 237 contextMap = (ThreadContextMap) clazz.newInstance(); 238 break; 239 } 240 } catch (final ClassNotFoundException cnfe) { 241 LOGGER.error("Unable to locate configured LoggerContextFactory {}", threadContextMapName); 242 contextMap = new DefaultThreadContextMap(useMap); 243 } catch (final Exception ex) { 244 LOGGER.error("Unable to create configured LoggerContextFactory {}", threadContextMapName, ex); 245 contextMap = new DefaultThreadContextMap(useMap); 246 } 247 } 248 } 249 } 250 if (contextMap == null) { 251 contextMap = new DefaultThreadContextMap(useMap); 252 } 253 } 254 255 private ThreadContext() { 256 // empty 257 } 258 259 /** 260 * Puts a context value (the <code>value</code> parameter) as identified 261 * with the <code>key</code> parameter into the current thread's 262 * context map. 263 * <p/> 264 * <p>If the current thread does not have a context map it is 265 * created as a side effect. 266 * @param key The key name. 267 * @param value The key value. 268 */ 269 public static void put(final String key, final String value) { 270 contextMap.put(key, value); 271 } 272 273 /** 274 * Gets the context value identified by the <code>key</code> parameter. 275 * <p/> 276 * <p>This method has no side effects. 277 * @param key The key to locate. 278 * @return The value associated with the key or null. 279 */ 280 public static String get(final String key) { 281 return contextMap.get(key); 282 } 283 284 /** 285 * Removes the context value identified by the <code>key</code> parameter. 286 * @param key The key to remove. 287 */ 288 public static void remove(final String key) { 289 contextMap.remove(key); 290 } 291 292 /** 293 * Clears the context map. 294 */ 295 public static void clearMap() { 296 contextMap.clear(); 297 } 298 299 /** 300 * Clears the context map and stack. 301 */ 302 public static void clearAll() { 303 clearMap(); 304 clearStack(); 305 } 306 307 /** 308 * Determines if the key is in the context. 309 * @param key The key to locate. 310 * @return True if the key is in the context, false otherwise. 311 */ 312 public static boolean containsKey(final String key) { 313 return contextMap.containsKey(key); 314 } 315 316 /** 317 * Returns a mutable copy of current thread's context Map. 318 * @return a mutable copy of the context. 319 */ 320 public static Map<String, String> getContext() { 321 return contextMap.getCopy(); 322 } 323 324 /** 325 * Returns an immutable view of the current thread's context Map. 326 * @return An immutable view of the ThreadContext Map. 327 */ 328 public static Map<String, String> getImmutableContext() { 329 final Map<String, String> map = contextMap.getImmutableMapOrNull(); 330 return map == null ? EMPTY_MAP : map; 331 } 332 333 /** 334 * Returns true if the Map is empty. 335 * @return true if the Map is empty, false otherwise. 336 */ 337 public static boolean isEmpty() { 338 return contextMap.isEmpty(); 339 } 340 341 /** 342 * Clears the stack for this thread. 343 */ 344 public static void clearStack() { 345 contextStack.clear(); 346 } 347 348 /** 349 * Returns a copy of this thread's stack. 350 * @return A copy of this thread's stack. 351 */ 352 public static ContextStack cloneStack() { 353 return contextStack.copy(); 354 } 355 356 /** 357 * Gets an immutable copy of this current thread's context stack. 358 * @return an immutable copy of the ThreadContext stack. 359 */ 360 public static ContextStack getImmutableStack() { 361 ContextStack result = contextStack.getImmutableStackOrNull(); 362 return result == null ? EMPTY_STACK : result; 363 } 364 365 /** 366 * Sets this thread's stack. 367 * @param stack The stack to use. 368 */ 369 public static void setStack(final Collection<String> stack) { 370 if (stack.isEmpty() || !useStack) { 371 return; 372 } 373 contextStack.clear(); 374 contextStack.addAll(stack); 375 } 376 377 /** 378 * Gets the current nesting depth of this thread's stack. 379 * @return the number of items in the stack. 380 * 381 * @see #trim 382 */ 383 public static int getDepth() { 384 return contextStack.getDepth(); 385 } 386 387 /** 388 * Returns the value of the last item placed on the stack. 389 * <p/> 390 * <p>The returned value is the value that was pushed last. If no 391 * context is available, then the empty string "" is returned. 392 * 393 * @return String The innermost diagnostic context. 394 */ 395 public static String pop() { 396 return contextStack.pop(); 397 } 398 399 /** 400 * Looks at the last diagnostic context at the top of this NDC 401 * without removing it. 402 * <p/> 403 * <p>The returned value is the value that was pushed last. If no 404 * context is available, then the empty string "" is returned. 405 * 406 * @return String The innermost diagnostic context. 407 */ 408 public static String peek() { 409 return contextStack.peek(); 410 } 411 412 /** 413 * Pushes new diagnostic context information for the current thread. 414 * <p/> 415 * <p>The contents of the <code>message</code> parameter is 416 * determined solely by the client. 417 * 418 * @param message The new diagnostic context information. 419 */ 420 public static void push(final String message) { 421 contextStack.push(message); 422 } 423 /** 424 * Pushes new diagnostic context information for the current thread. 425 * <p/> 426 * <p>The contents of the <code>message</code> and args parameters are 427 * determined solely by the client. The message will be treated as a format String 428 * and tokens will be replaced with the String value of the arguments in accordance 429 * with ParameterizedMessage. 430 * 431 * @param message The new diagnostic context information. 432 * @param args Parameters for the message. 433 */ 434 public static void push(final String message, final Object... args) { 435 contextStack.push(ParameterizedMessage.format(message, args)); 436 } 437 438 /** 439 * Removes the diagnostic context for this thread. 440 * <p/> 441 * <p>Each thread that created a diagnostic context by calling 442 * {@link #push} should call this method before exiting. Otherwise, 443 * the memory used by the <b>thread</b> cannot be reclaimed by the 444 * VM. 445 * <p/> 446 * <p>As this is such an important problem in heavy duty systems and 447 * because it is difficult to always guarantee that the remove 448 * method is called before exiting a thread, this method has been 449 * augmented to lazily remove references to dead threads. In 450 * practice, this means that you can be a little sloppy and 451 * occasionally forget to call {@link #remove} before exiting a 452 * thread. However, you must call <code>remove</code> sometime. If 453 * you never call it, then your application is sure to run out of 454 * memory. 455 */ 456 public static void removeStack() { 457 contextStack.clear(); 458 } 459 460 /** 461 * Trims elements from this diagnostic context. If the current 462 * depth is smaller or equal to <code>maxDepth</code>, then no 463 * action is taken. If the current depth is larger than newDepth 464 * then all elements at maxDepth or higher are discarded. 465 * <p/> 466 * <p>This method is a convenient alternative to multiple {@link 467 * #pop} calls. Moreover, it is often the case that at the end of 468 * complex call sequences, the depth of the ThreadContext is 469 * unpredictable. The <code>trim</code> method circumvents 470 * this problem. 471 * <p/> 472 * <p>For example, the combination 473 * <pre> 474 * void foo() { 475 * int depth = ThreadContext.getDepth(); 476 * <p/> 477 * ... complex sequence of calls 478 * <p/> 479 * ThreadContext.trim(depth); 480 * } 481 * </pre> 482 * <p/> 483 * ensures that between the entry and exit of foo the depth of the 484 * diagnostic stack is conserved. 485 * 486 * @see #getDepth 487 * @param depth The number of elements to keep. 488 */ 489 public static void trim(final int depth) { 490 contextStack.trim(depth); 491 } 492 493 /** 494 * The ThreadContext Stack interface. 495 */ 496 public interface ContextStack extends Serializable, Collection<String> { 497 498 /** 499 * Returns the element at the top of the stack. 500 * @return The element at the top of the stack. 501 * @throws java.util.NoSuchElementException if the stack is empty. 502 */ 503 String pop(); 504 505 /** 506 * Returns the element at the top of the stack without removing it or null if the stack is empty. 507 * @return the element at the top of the stack or null if the stack is empty. 508 */ 509 String peek(); 510 511 /** 512 * Pushes an element onto the stack. 513 * @param message The element to add. 514 */ 515 void push(String message); 516 517 /** 518 * Returns the number of elements in the stack. 519 * @return the number of elements in the stack. 520 */ 521 int getDepth(); 522 523 /** 524 * Returns all the elements in the stack in a List. 525 * @return all the elements in the stack in a List. 526 */ 527 List<String> asList(); 528 529 /** 530 * Trims elements from the end of the stack. 531 * @param depth The maximum number of items in the stack to keep. 532 */ 533 void trim(int depth); 534 535 /** 536 * Returns a copy of the ContextStack. 537 * @return a copy of the ContextStack. 538 */ 539 ContextStack copy(); 540 541 /** 542 * Returns a ContextStack with the same contents as this ContextStack or {@code null}. 543 * Attempts to modify the returned stack may or may not throw an exception, but will not affect the contents 544 * of this ContextStack. 545 * @return a ContextStack with the same contents as this ContextStack or {@code null}. 546 */ 547 ContextStack getImmutableStackOrNull(); 548 } 549 }