001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.classic.util; 015 016import java.util.Collections; 017import java.util.Deque; 018import java.util.HashMap; 019import java.util.Map; 020import java.util.Set; 021 022import org.slf4j.helpers.ThreadLocalMapOfStacks; 023import org.slf4j.spi.MDCAdapter; 024 025/** 026 * A <em>Mapped Diagnostic Context</em>, or MDC in short, is an instrument for 027 * distinguishing interleaved log output from different sources. Log output is 028 * typically interleaved when a server handles multiple clients 029 * near-simultaneously. 030 * <p/> 031 * <b><em>The MDC is managed on a per thread basis</em></b>. Note that a child 032 * thread <b>does not</b> inherit the mapped diagnostic context of its parent. 033 * <p/> 034 * <p/> 035 * For more information about MDC, please refer to the online manual at 036 * http://logback.qos.ch/manual/mdc.html 037 * 038 * @author Ceki Gülcü 039 */ 040public class LogbackMDCAdapterSimple implements MDCAdapter { 041 042 043 // We wish to avoid unnecessarily copying of the map. To ensure 044 // efficient/timely copying, we have a variable keeping track of the previous 045 // operation. A copy is necessary on 'put' or 'remove' but only if the last 046 // operation was a 'get'. Get operations never necessitate a copy nor 047 // successive 'put/remove' operations, only a get followed by a 'put/remove' 048 // requires copying the map. 049 // See http://jira.qos.ch/browse/LOGBACK-620 for the original discussion. 050 051 // We no longer use CopyOnInheritThreadLocal in order to solve LBCLASSIC-183 052 // Initially the contents of the thread local in parent and child threads 053 // reference the same map. However, as soon as a thread invokes the put() 054 // method, the maps diverge as they should. 055// final ThreadLocal<Map<String, String>> modifiableMap = new ThreadLocal<Map<String, String>>(); 056 057 final ThreadLocal<Map<String, String>> threadLocalUnmodifiableMap = new ThreadLocal<Map<String, String>>(); 058 059// private static final int WRITE_OPERATION = 1; 060// private static final int MAP_COPY_OPERATION = 2; 061 062 // keeps track of the previous operation performed 063// final ThreadLocal<Integer> previousOperation = new ThreadLocal<Integer>(); 064 065 private final ThreadLocalMapOfStacks threadLocalMapOfDeques = new ThreadLocalMapOfStacks(); 066 067// private Integer getAndSetPreviousOperation(int op) { 068// Integer penultimateOp = previousOperation.get(); 069// previousOperation.set(op); 070// return penultimateOp; 071// } 072 073// private boolean wasPreviousOpReadOrNull(Integer lastOp) { 074// return lastOp == null || lastOp.intValue() == MAP_COPY_OPERATION; 075// } 076 077// private Map<String, String> duplicateAndInsertNewMap(Map<String, String> oldMap) { 078// Map<String, String> newMap = duplicateOldMap(oldMap); 079// modifiableMap.set(newMap); 080// return newMap; 081// } 082 083 private Map<String, String> duplicateMap(Map<String, String> oldMap) { 084 if(oldMap != null) 085 return new HashMap<>(oldMap); 086 else 087 return new HashMap<>(); 088 } 089 090 /** 091 * Put a context value (the <code>val</code> parameter) as identified with the 092 * <code>key</code> parameter into the current thread's context map. Note that 093 * contrary to log4j, the <code>val</code> parameter can be null. 094 * <p/> 095 * <p/> 096 * If the current thread does not have a context map it is created as a side 097 * effect of this call. 098 * 099 * @throws IllegalArgumentException in case the "key" parameter is null 100 */ 101 public void put(String key, String val) throws IllegalArgumentException { 102 if (key == null) { 103 throw new IllegalArgumentException("key cannot be null"); 104 } 105 106 Map<String, String> oldMap = threadLocalUnmodifiableMap.get(); 107 Map<String, String> newMap = duplicateMap(oldMap); 108 newMap.put(key, val); 109 makeUnmodifiableAndThreadLocalSet(newMap); 110 } 111 112 private void makeUnmodifiableAndThreadLocalSet(Map<String, String> aMap) { 113 Map<String, String> unmodifiable = Collections.unmodifiableMap(aMap); 114 threadLocalUnmodifiableMap.set(unmodifiable); 115 } 116 117 /** 118 * Remove the context identified by the <code>key</code> parameter. 119 * <p/> 120 */ 121 public void remove(String key) { 122 if (key == null) { 123 return; 124 } 125 Map<String, String> oldMap = threadLocalUnmodifiableMap.get(); 126 if (oldMap == null) 127 return; 128 129 Map<String, String> newMap = duplicateMap(oldMap); 130 newMap.remove(key); 131 makeUnmodifiableAndThreadLocalSet(newMap); 132 } 133 134 /** 135 * Clear all entries in the MDC. 136 */ 137 public void clear() { 138 threadLocalUnmodifiableMap.remove(); 139 } 140 141 /** 142 * Get the context identified by the <code>key</code> parameter. 143 * <p/> 144 */ 145 public String get(String key) { 146 final Map<String, String> map = threadLocalUnmodifiableMap.get(); 147 if ((map != null) && (key != null)) { 148 return map.get(key); 149 } else { 150 return null; 151 } 152 } 153 154 /** 155 * Get the current thread's MDC as a map. This method is intended to be used 156 * internally. 157 */ 158 public Map<String, String> getPropertyMap() { 159 return threadLocalUnmodifiableMap.get(); 160 } 161 162 /** 163 * Returns the keys in the MDC as a {@link Set}. The returned value can be null. 164 */ 165 public Set<String> getKeys() { 166 Map<String, String> map = getPropertyMap(); 167 168 if (map != null) { 169 return map.keySet(); 170 } else { 171 return null; 172 } 173 } 174 175 /** 176 * Return a copy of the current thread's context map. Returned value may be 177 * null. 178 */ 179 public Map<String, String> getCopyOfContextMap() { 180 Map<String, String> hashMap = threadLocalUnmodifiableMap.get(); 181 return duplicateMap(hashMap); 182 } 183 184 /** 185 * Set the MDC map to the map passed as parameter. 186 * 187 * @param contextMap the new map 188 */ 189 public void setContextMap(Map<String, String> contextMap) { 190 duplicateMap(contextMap); 191 } 192 193 @Override 194 public void pushByKey(String key, String value) { 195 threadLocalMapOfDeques.pushByKey(key, value); 196 } 197 198 @Override 199 public String popByKey(String key) { 200 return threadLocalMapOfDeques.popByKey(key); 201 } 202 203 @Override 204 public Deque<String> getCopyOfDequeByKey(String key) { 205 return threadLocalMapOfDeques.getCopyOfDequeByKey(key); 206 } 207 @Override 208 public void clearDequeByKey(String key) { 209 threadLocalMapOfDeques.clearDequeByKey(key); 210 } 211}