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