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 org.apache.logging.log4j.message.ParameterizedMessage; 021 022 import java.io.Serializable; 023 import java.util.ArrayList; 024 import java.util.Collection; 025 import java.util.HashMap; 026 import java.util.List; 027 import java.util.Map; 028 import java.util.NoSuchElementException; 029 030 /** 031 * The ThreadContext allows applications to store information either in a Map 032 * <p/> 033 * <p><b><em>The MDC is managed on a per thread basis</em></b>. A 034 * child thread automatically inherits a <em>copy</em> of the mapped 035 * diagnostic context of its parent. 036 */ 037 public final class ThreadContext { 038 039 /** 040 * Empty, immutable Map. 041 */ 042 public static final Map<String, String> EMPTY_MAP = new ImmutableMap(); 043 044 /** 045 * Empty, immutable ContextStack. 046 */ 047 public static final ContextStack EMPTY_STACK = new ImmutableStack(); 048 049 private static final String DISABLE_MAP = "disableThreadContextMap"; 050 051 private static final String DISABLE_STACK = "disableThreadContextStack"; 052 053 private static final String DISABLE_ALL = "disableThreadContext"; 054 055 private static boolean all = Boolean.getBoolean(DISABLE_ALL); 056 057 private static boolean useMap = !(Boolean.getBoolean(DISABLE_MAP) || all); 058 059 private static boolean useStack = !(Boolean.getBoolean(DISABLE_STACK) || all); 060 061 private static ThreadLocal<Map<String, String>> localMap = 062 new InheritableThreadLocal<Map<String, String>>() { 063 @Override 064 protected Map<String, String> childValue(Map<String, String> parentValue) { 065 return parentValue == null || !useMap ? null : new HashMap<String, String>(parentValue); 066 } 067 }; 068 069 private static ThreadLocal<ContextStack> localStack = new ThreadLocal<ContextStack>(); 070 071 private ThreadContext() { 072 073 } 074 075 /** 076 * Put a context value (the <code>o</code> parameter) as identified 077 * with the <code>key</code> parameter into the current thread's 078 * context map. 079 * <p/> 080 * <p>If the current thread does not have a context map it is 081 * created as a side effect. 082 * @param key The key name. 083 * @param value The key value. 084 */ 085 public static void put(String key, String value) { 086 if (!useMap) { 087 return; 088 } 089 Map<String, String> map = localMap.get(); 090 if (map == null) { 091 map = new HashMap<String, String>(); 092 localMap.set(map); 093 } 094 map.put(key, value); 095 } 096 097 /** 098 * Get the context identified by the <code>key</code> parameter. 099 * <p/> 100 * <p>This method has no side effects. 101 * @param key The key to locate. 102 * @return The value associated with the key or null. 103 */ 104 public static String get(String key) { 105 Map<String, String> map = localMap.get(); 106 return map == null ? null : map.get(key); 107 } 108 109 /** 110 * Remove the the context identified by the <code>key</code> 111 * parameter. 112 * @param key The key to remove. 113 */ 114 public static void remove(String key) { 115 Map<String, String> map = localMap.get(); 116 if (map != null) { 117 map.remove(key); 118 } 119 } 120 121 /** 122 * Clear the context. 123 */ 124 public static void clear() { 125 localMap.remove(); 126 } 127 128 /** 129 * Determine if the key is in the context. 130 * @param key The key to locate. 131 * @return True if the key is in the context, false otherwise. 132 */ 133 public static boolean containsKey(String key) { 134 Map<String, String> map = localMap.get(); 135 return map == null ? false : map.containsKey(key); 136 } 137 138 /** 139 * Get a copy of current thread's context Map. 140 * @return a copy of the context. 141 */ 142 public static Map<String, String> getContext() { 143 Map<String, String> map = localMap.get(); 144 return map == null ? new HashMap<String, String>() : new HashMap<String, String>(map); 145 } 146 147 /** 148 * Get an immutable copy of the current thread's context Map. 149 * @return An immutable copy of the ThreadContext Map. 150 */ 151 public static Map<String, String> getImmutableContext() { 152 Map<String, String> map = localMap.get(); 153 return map == null ? new ImmutableMap() : new ImmutableMap(map); 154 } 155 156 /** 157 * Returns true if the Map is empty. 158 * @return true if the Map is empty, false otherwise. 159 */ 160 public static boolean isEmpty() { 161 Map<String, String> map = localMap.get(); 162 return map == null || map.size() == 0; 163 } 164 165 /** 166 * Clear the stack for this thread. 167 */ 168 public static void clearStack() { 169 localStack.remove(); 170 } 171 172 /** 173 * Returns a copy of this thread's stack. 174 * @return A copy of this thread's stack. 175 */ 176 public static ContextStack cloneStack() { 177 ContextStack stack = localStack.get(); 178 return stack == null ? new ThreadContextStack() : new ThreadContextStack(stack.asList()); 179 } 180 181 /** 182 * Get an immutable copy of this current thread's context stack. 183 * @return an immutable copy of the ThreadContext stack. 184 */ 185 public static ContextStack getImmutableStack() { 186 ContextStack stack = localStack.get(); 187 return stack == null ? EMPTY_STACK : new ImmutableStack(stack.asList()); 188 } 189 190 /** 191 * Set this thread's stack. 192 * @param stack The stack to use. 193 */ 194 public static void setStack(Collection<String> stack) { 195 if (stack.size() == 0 || !useStack) { 196 return; 197 } 198 localStack.set(new ThreadContextStack(stack)); 199 } 200 201 /** 202 * Get the current nesting depth of this thread's stack. 203 * @return the number of items in the stack. 204 * 205 * @see #trim 206 */ 207 public static int getDepth() { 208 ContextStack stack = localStack.get(); 209 return stack == null ? 0 : stack.getDepth(); 210 } 211 212 /** 213 * Returns the value of the last item placed on the stack. 214 * <p/> 215 * <p>The returned value is the value that was pushed last. If no 216 * context is available, then the empty string "" is returned. 217 * 218 * @return String The innermost diagnostic context. 219 */ 220 public static String pop() { 221 ContextStack s = localStack.get(); 222 if (s == null || s.getDepth() == 0) { 223 return ""; 224 } 225 return s.pop(); 226 } 227 228 /** 229 * Looks at the last diagnostic context at the top of this NDC 230 * without removing it. 231 * <p/> 232 * <p>The returned value is the value that was pushed last. If no 233 * context is available, then the empty string "" is returned. 234 * 235 * @return String The innermost diagnostic context. 236 */ 237 public static String peek() { 238 ContextStack s = localStack.get(); 239 if (s == null || s.getDepth() == 0) { 240 return ""; 241 } 242 return s.peek(); 243 } 244 245 /** 246 * Push new diagnostic context information for the current thread. 247 * <p/> 248 * <p>The contents of the <code>message</code> parameter is 249 * determined solely by the client. 250 * 251 * @param message The new diagnostic context information. 252 */ 253 public static void push(String message) { 254 if (!useStack) { 255 return; 256 } 257 ContextStack stack = localStack.get(); 258 if (stack == null) { 259 stack = new ThreadContextStack(); 260 localStack.set(stack); 261 } 262 stack.push(message); 263 } 264 /** 265 * Push new diagnostic context information for the current thread. 266 * <p/> 267 * <p>The contents of the <code>message</code> and args parameters are 268 * determined solely by the client. The message will be treated as a format String 269 * and tokens will be replaced with the String value of the arguments in accordance 270 * with ParameterizedMessage. 271 * 272 * @param message The new diagnostic context information. 273 */ 274 public static void push(String message, Object... args) { 275 if (!useStack) { 276 return; 277 } 278 ContextStack stack = localStack.get(); 279 if (stack == null) { 280 stack = new ThreadContextStack(); 281 localStack.set(stack); 282 } 283 stack.push(ParameterizedMessage.format(message, args)); 284 } 285 286 /** 287 * Remove the diagnostic context for this thread. 288 * <p/> 289 * <p>Each thread that created a diagnostic context by calling 290 * {@link #push} should call this method before exiting. Otherwise, 291 * the memory used by the <b>thread</b> cannot be reclaimed by the 292 * VM. 293 * <p/> 294 * <p>As this is such an important problem in heavy duty systems and 295 * because it is difficult to always guarantee that the remove 296 * method is called before exiting a thread, this method has been 297 * augmented to lazily remove references to dead threads. In 298 * practice, this means that you can be a little sloppy and 299 * occasionally forget to call {@link #remove} before exiting a 300 * thread. However, you must call <code>remove</code> sometime. If 301 * you never call it, then your application is sure to run out of 302 * memory. 303 */ 304 public static void removeStack() { 305 localStack.remove(); 306 } 307 308 /** 309 * Trims elements from this diagnostic context. If the current 310 * depth is smaller or equal to <code>maxDepth</code>, then no 311 * action is taken. If the current depth is larger than newDepth 312 * then all elements at maxDepth or higher are discarded. 313 * <p/> 314 * <p>This method is a convenient alternative to multiple {@link 315 * #pop} calls. Moreover, it is often the case that at the end of 316 * complex call sequences, the depth of the ThreadContext is 317 * unpredictable. The <code>trim</code> method circumvents 318 * this problem. 319 * <p/> 320 * <p>For example, the combination 321 * <pre> 322 * void foo() { 323 * int depth = ThreadContext.getDepth(); 324 * <p/> 325 * ... complex sequence of calls 326 * <p/> 327 * ThreadContext.trim(depth); 328 * } 329 * </pre> 330 * <p/> 331 * ensures that between the entry and exit of foo the depth of the 332 * diagnostic stack is conserved. 333 * 334 * @see #getDepth 335 * @param depth The number of elements to keep. 336 */ 337 public static void trim(int depth) { 338 ContextStack stack = localStack.get(); 339 if (stack != null) { 340 stack.trim(depth); 341 } 342 } 343 344 /** 345 * The ThreadContext Stack interface. 346 */ 347 public interface ContextStack extends Serializable { 348 349 /** 350 * Clears all elements from the stack. 351 */ 352 void clear(); 353 354 /** 355 * Returns the element at the top of the stack. 356 * @return The element at the top of the stack. 357 * @throws java.util.NoSuchElementException if the stack is empty. 358 */ 359 String pop(); 360 361 /** 362 * Returns the element at the top of the stack without removing it or null if the stack is empty. 363 * @return the element at the top of the stack or null if the stack is empty. 364 */ 365 String peek(); 366 367 /** 368 * Add an element to the stack. 369 * @param message The element to add. 370 */ 371 void push(String message); 372 373 /** 374 * Returns the number of elements in the stack. 375 * @return the number of elements in the stack. 376 */ 377 int getDepth(); 378 379 /** 380 * Returns all the elements in the stack in a List. 381 * @return all the elements in the stack in a List. 382 */ 383 List<String> asList(); 384 385 /** 386 * Trims elements from the end of the stack. 387 * @param depth The maximum number of items in the stack to keep. 388 */ 389 void trim(int depth); 390 391 /** 392 * Returns a copy of the ContextStack. 393 * @return a copy of the ContextStack. 394 */ 395 ContextStack copy(); 396 } 397 398 /** 399 * The ContextStack implementation. 400 */ 401 private static class ThreadContextStack extends ArrayList<String> implements ContextStack { 402 403 private static final long serialVersionUID = 5050501L; 404 405 public ThreadContextStack() { 406 super(); 407 } 408 409 public ThreadContextStack(Collection<String> collection) { 410 super(collection); 411 } 412 413 public String pop() { 414 int index = size() - 1; 415 if (index >= 0) { 416 String result = get(index); 417 remove(index); 418 return result; 419 } 420 throw new NoSuchElementException("The ThreadContext stack is empty"); 421 } 422 423 public String peek() { 424 int index = size() - 1; 425 if (index >= 0) { 426 return get(index); 427 } 428 return null; 429 } 430 431 public void push(String message) { 432 add(message); 433 } 434 435 public int getDepth() { 436 return size(); 437 } 438 439 public List<String> asList() { 440 return this; 441 } 442 443 public void trim(int depth) { 444 if (depth < 0) { 445 throw new IllegalArgumentException("Maximum stack depth cannot be negative"); 446 } 447 while (size() > depth) { 448 remove(size() - 1); 449 } 450 451 } 452 453 public ContextStack copy() { 454 return new ThreadContextStack(this); 455 } 456 } 457 458 /** 459 * An immutable ContextStack. 460 */ 461 public static class ImmutableStack extends ThreadContextStack { 462 463 private static final long serialVersionUID = 5050502L; 464 465 public ImmutableStack() { 466 } 467 468 public ImmutableStack(Collection<String> collection) { 469 super(collection); 470 } 471 472 public ImmutableStack(ThreadContextStack stack) { 473 super(stack); 474 } 475 476 @Override 477 public void push(String message) { 478 throw new UnsupportedOperationException("Stack cannot be modified"); 479 } 480 481 @Override 482 public void trim(int depth) { 483 throw new UnsupportedOperationException("Stack cannot be modified"); 484 } 485 } 486 487 /** 488 * An immutable Context Map. 489 */ 490 public static class ImmutableMap extends HashMap<String, String> { 491 private static final long serialVersionUID = 5050503L; 492 493 public ImmutableMap() { 494 super(); 495 } 496 497 public ImmutableMap(Map<String, String> map) { 498 super(map); 499 } 500 501 @Override 502 public String put(String s, String s1) { 503 throw new UnsupportedOperationException("Map cannot be modified"); 504 } 505 506 @Override 507 public void putAll(Map<? extends String, ? extends String> map) { 508 throw new UnsupportedOperationException("Map cannot be modified"); 509 } 510 511 @Override 512 public String remove(Object o) { 513 throw new UnsupportedOperationException("Map cannot be modified"); 514 } 515 516 @Override 517 public void clear() { 518 throw new UnsupportedOperationException("Map cannot be modified"); 519 } 520 } 521 }