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 */ 017package org.apache.logging.log4j; 018 019import java.util.HashMap; 020import java.util.Iterator; 021import java.util.List; 022import java.util.Map; 023 024/** 025 * Adds entries to the {@link ThreadContext stack or map} and them removes them when the object is closed, e.g. as part 026 * of a try-with-resources. User code can now look like this: 027 * <pre> 028 * try (final CloseableThreadContext.Instance ignored = CloseableThreadContext.put(key1, value1).put(key2, value2)) { 029 * callSomeMethodThatLogsALot(); 030 * 031 * // Entries for key1 and key2 are automatically removed from the ThreadContext map when done. 032 * } 033 * </pre> 034 * 035 * @since 2.6 036 */ 037public class CloseableThreadContext { 038 039 private CloseableThreadContext() { 040 } 041 042 /** 043 * Pushes new diagnostic context information on to the Thread Context Stack. The information will be popped off when 044 * the instance is closed. 045 * 046 * @param message The new diagnostic context information. 047 * @return a new instance that will back out the changes when closed. 048 */ 049 public static CloseableThreadContext.Instance push(final String message) { 050 return new CloseableThreadContext.Instance().push(message); 051 } 052 053 /** 054 * Pushes new diagnostic context information on to the Thread Context Stack. The information will be popped off when 055 * the instance is closed. 056 * 057 * @param message The new diagnostic context information. 058 * @param args Parameters for the message. 059 * @return a new instance that will back out the changes when closed. 060 */ 061 public static CloseableThreadContext.Instance push(final String message, final Object... args) { 062 return new CloseableThreadContext.Instance().push(message, args); 063 } 064 065 /** 066 * Populates the Thread Context Map with the supplied key/value pair. Any existing key in the 067 * {@link ThreadContext} will be replaced with the supplied value, and restored back to their original value when 068 * the instance is closed. 069 * 070 * @param key The key to be added 071 * @param value The value to be added 072 * @return a new instance that will back out the changes when closed. 073 */ 074 public static CloseableThreadContext.Instance put(final String key, final String value) { 075 return new CloseableThreadContext.Instance().put(key, value); 076 } 077 078 /** 079 * Populates the Thread Context Stack with the supplied stack. The information will be popped off when 080 * the instance is closed. 081 * 082 * @param messages The list of messages to be added 083 * @return a new instance that will back out the changes when closed. 084 * @since 2.8 085 */ 086 public static CloseableThreadContext.Instance pushAll(final List<String> messages) { 087 return new CloseableThreadContext.Instance().pushAll(messages); 088 } 089 090 /** 091 * Populates the Thread Context Map with the supplied key/value pairs. Any existing keys in the 092 * {@link ThreadContext} will be replaced with the supplied values, and restored back to their original value when 093 * the instance is closed. 094 * 095 * @param values The map of key/value pairs to be added 096 * @return a new instance that will back out the changes when closed. 097 * @since 2.8 098 */ 099 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}