1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache license, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the license for the specific language governing permissions and 15 * limitations under the license. 16 */ 17 package org.apache.logging.log4j; 18 19 import java.util.HashMap; 20 import java.util.Iterator; 21 import java.util.List; 22 import java.util.Map; 23 24 /** 25 * Adds entries to the {@link ThreadContext stack or map} and them removes them when the object is closed, e.g. as part 26 * of a try-with-resources. User code can now look like this: 27 * <pre> 28 * try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.put(key1, value1).put(key2, value2)) { 29 * callSomeMethodThatLogsALot(); 30 * 31 * // Entries for key1 and key2 are automatically removed from the ThreadContext map when done. 32 * } 33 * </pre> 34 * 35 * @since 2.6 36 */ 37 public class CloseableThreadContext { 38 39 private CloseableThreadContext() { 40 } 41 42 /** 43 * Pushes new diagnostic context information on to the Thread Context Stack. The information will be popped off when 44 * the instance is closed. 45 * 46 * @param message The new diagnostic context information. 47 * @return a new instance that will back out the changes when closed. 48 */ 49 public static CloseableThreadContext.Instance push(final String message) { 50 return new CloseableThreadContext.Instance().push(message); 51 } 52 53 /** 54 * Pushes new diagnostic context information on to the Thread Context Stack. The information will be popped off when 55 * the instance is closed. 56 * 57 * @param message The new diagnostic context information. 58 * @param args Parameters for the message. 59 * @return a new instance that will back out the changes when closed. 60 */ 61 public static CloseableThreadContext.Instance push(final String message, final Object... args) { 62 return new CloseableThreadContext.Instance().push(message, args); 63 } 64 65 /** 66 * Populates the Thread Context Map with the supplied key/value pair. Any existing key in the 67 * {@link ThreadContext} will be replaced with the supplied value, and restored back to their original value when 68 * the instance is closed. 69 * 70 * @param key The key to be added 71 * @param value The value to be added 72 * @return a new instance that will back out the changes when closed. 73 */ 74 public static CloseableThreadContext.Instance put(final String key, final String value) { 75 return new CloseableThreadContext.Instance().put(key, value); 76 } 77 78 /** 79 * Populates the Thread Context Stack with the supplied stack. The information will be popped off when 80 * the instance is closed. 81 * 82 * @param messages The list of messages to be added 83 * @return a new instance that will back out the changes when closed. 84 * @since 2.8 85 */ 86 public static CloseableThreadContext.Instance pushAll(final List<String> messages) { 87 return new CloseableThreadContext.Instance().pushAll(messages); 88 } 89 90 /** 91 * Populates the Thread Context Map with the supplied key/value pairs. Any existing keys in the 92 * {@link ThreadContext} will be replaced with the supplied values, and restored back to their original value when 93 * the instance is closed. 94 * 95 * @param values The map of key/value pairs to be added 96 * @return a new instance that will back out the changes when closed. 97 * @since 2.8 98 */ 99 public static CloseableThreadContext.Instance putAll(final Map<String, String> values) { 100 return new CloseableThreadContext.Instance().putAll(values); 101 } 102 103 public static class Instance implements AutoCloseable { 104 105 private int pushCount = 0; 106 private final Map<String, String> originalValues = new HashMap<>(); 107 108 private Instance() { 109 } 110 111 /** 112 * Pushes new diagnostic context information on to the Thread Context Stack. The information will be popped off when 113 * the instance is closed. 114 * 115 * @param message The new diagnostic context information. 116 * @return the instance that will back out the changes when closed. 117 */ 118 public Instance push(final String message) { 119 ThreadContext.push(message); 120 pushCount++; 121 return this; 122 } 123 124 /** 125 * Pushes new diagnostic context information on to the Thread Context Stack. The information will be popped off when 126 * the instance is closed. 127 * 128 * @param message The new diagnostic context information. 129 * @param args Parameters for the message. 130 * @return the instance that will back out the changes when closed. 131 */ 132 public Instance push(final String message, final Object[] args) { 133 ThreadContext.push(message, args); 134 pushCount++; 135 return this; 136 } 137 138 /** 139 * Populates the Thread Context Map with the supplied key/value pair. Any existing key in the 140 * {@link ThreadContext} will be replaced with the supplied value, and restored back to their original value when 141 * the instance is closed. 142 * 143 * @param key The key to be added 144 * @param value The value to be added 145 * @return a new instance that will back out the changes when closed. 146 */ 147 public Instance put(final String key, final String value) { 148 // If there are no existing values, a null will be stored as an old value 149 if (!originalValues.containsKey(key)) { 150 originalValues.put(key, ThreadContext.get(key)); 151 } 152 ThreadContext.put(key, value); 153 return this; 154 } 155 156 /** 157 * Populates the Thread Context Map with the supplied key/value pairs. Any existing keys in the 158 * {@link ThreadContext} will be replaced with the supplied values, and restored back to their original value when 159 * the instance is closed. 160 * 161 * @param values The map of key/value pairs to be added 162 * @return a new instance that will back out the changes when closed. 163 * @since 2.8 164 */ 165 public Instance putAll(final Map<String, String> values) { 166 final Map<String, String> currentValues = ThreadContext.getContext(); 167 ThreadContext.putAll(values); 168 for (final String key : values.keySet()) { 169 if (!originalValues.containsKey(key)) { 170 originalValues.put(key, currentValues.get(key)); 171 } 172 } 173 return this; 174 } 175 176 /** 177 * Populates the Thread Context Stack with the supplied stack. The information will be popped off when 178 * the instance is closed. 179 * 180 * @param messages The list of messages to be added 181 * @return a new instance that will back out the changes when closed. 182 * @since 2.8 183 */ 184 public Instance pushAll(final List<String> messages) { 185 for (final String message : messages) { 186 push(message); 187 } 188 return this; 189 } 190 191 /** 192 * Removes the values from the {@link ThreadContext}. 193 * <p> 194 * Values pushed to the {@link ThreadContext} <em>stack</em> will be popped off. 195 * </p> 196 * <p> 197 * Values put on the {@link ThreadContext} <em>map</em> will be removed, or restored to their original values it they already existed. 198 * </p> 199 */ 200 @Override 201 public void close() { 202 closeStack(); 203 closeMap(); 204 } 205 206 private void closeMap() { 207 for (final Iterator<Map.Entry<String, String>> it = originalValues.entrySet().iterator(); it.hasNext(); ) { 208 final Map.Entry<String, String> entry = it.next(); 209 final String key = entry.getKey(); 210 final String originalValue = entry.getValue(); 211 if (null == originalValue) { 212 ThreadContext.remove(key); 213 } else { 214 ThreadContext.put(key, originalValue); 215 } 216 it.remove(); 217 } 218 } 219 220 private void closeStack() { 221 for (int i = 0; i < pushCount; i++) { 222 ThreadContext.pop(); 223 } 224 pushCount = 0; 225 } 226 } 227 }