/** * Logback: the reliable, generic, fast and flexible logging framework. * Copyright (C) 1999-2009, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ package ch.qos.logback.classic.util; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.slf4j.spi.MDCAdapter; /** * A Mapped Diagnostic Context, or MDC in short, is an instrument for * distinguishing interleaved log output from different sources. Log output is * typically interleaved when a server handles multiple clients * near-simultaneously. *

* The MDC is managed on a per thread basis. A child thread * automatically inherits a copy of the mapped diagnostic context of * its parent. *

*

* For more information about MDC, please refer to the online manual at * http://logback.qos.ch/manual/mdc.html * * @author Ceki Gülcü */ public class LogbackMDCAdapter implements MDCAdapter { // LBCLASSIC-183 has not yet been checked final InheritableThreadLocal> currentThreadLocal = new InheritableThreadLocal>() { @SuppressWarnings("unchecked") @Override protected HashMap childValue(HashMap parentValue) { if (parentValue == null) { return null; } return super.childValue((HashMap)parentValue.clone()); } }; final InheritableThreadLocal> clonedThreadLocal = new InheritableThreadLocal>() { @Override protected Map childValue(Map parentValue) { return parentValue; } }; public LogbackMDCAdapter() { } /** * Put a context value (the val parameter) as identified with the * key parameter into the current thread's context map. Note that * contrary to log4j, the val parameter can be null. *

*

* If the current thread does not have a context map it is created as a side * effect of this call. *

*

* Each time a value is added, a new instance of the map is created. This is * to be certain that the serialization process will operate on the updated * map and not send a reference to the old map, thus not allowing the remote * logback component to see the latest changes. * * @throws IllegalArgumentException in case the "key" parameter is null */ public void put(String key, String val) throws IllegalArgumentException { if (key == null) { throw new IllegalArgumentException("key cannot be null"); } HashMap current = currentThreadLocal.get(); if (current == null) { current = new HashMap(); currentThreadLocal.set(current); } current.put(key, val); clonedThreadLocal.set(null); } /** * Get the context identified by the key parameter. *

*

* This method has no side effects. */ public String get(String key) { HashMap hashMap = currentThreadLocal.get(); if ((hashMap != null) && (key != null)) { return hashMap.get(key); } else { return null; } } /** * Remove the the context identified by the key parameter. *

*

* Each time a value is removed, a new instance of the map is created. This is * to be certain that the serialization process will operate on the updated * map and not send a reference to the old map, thus not allowing the remote * logback component to see the latest changes. */ public void remove(String key) { if (key == null) { return; } HashMap current = currentThreadLocal.get(); if (current != null) { current.remove(key); clonedThreadLocal.set(null); } } /** * Clear all entries in the MDC. */ public void clear() { currentThreadLocal.set(null); clonedThreadLocal.set(null); } /** * Get the current thread's MDC as a map. This method is intended to be used * internally. */ @SuppressWarnings("unchecked") public Map getPropertyMap() { Map cloned = clonedThreadLocal.get(); if (cloned == null) { HashMap current = currentThreadLocal.get(); if (current != null) { cloned = Collections.unmodifiableMap((HashMap)current.clone()); clonedThreadLocal.set(cloned); } } return cloned; } /** * Return a copy of the current thread's context map. Returned value may be * null. */ public Map getCopyOfContextMap() { Map hashMap = currentThreadLocal.get(); if (hashMap == null) { return null; } else { return new HashMap(hashMap); } } /** * Returns the keys in the MDC as a {@link Set}. The returned value can be * null. */ public Set getKeys() { Map hashMap = currentThreadLocal.get(); if (hashMap != null) { return hashMap.keySet(); } else { return null; } } @SuppressWarnings("unchecked") public void setContextMap(Map contextMap) { if (contextMap != null) { currentThreadLocal.set(new HashMap(contextMap)); } else { currentThreadLocal.set(null); } clonedThreadLocal.set(null); } }