package org.slf4j; import com.alibaba.ttl.TransmittableThreadLocal; import org.slf4j.spi.MDCAdapter; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * 石家庄喜高科技有限责任公司 版权所有 © Copyright 2020
* * @Description: 重构{@link LogbackMDCAdapter}类,搭配TransmittableThreadLocal实现父子线程之间的数据传递
* @Project:
* @CreateDate: Created in 2020/2/4 16:05
* @Author: liuke */ public class TtlMDCAdapter implements MDCAdapter { private final ThreadLocal> copyOnInheritThreadLocal = new TransmittableThreadLocal<>(); private static final int WRITE_OPERATION = 1; private static final int MAP_COPY_OPERATION = 2; private static TtlMDCAdapter mtcMDCAdapter; /** * keeps track of the last operation performed */ private final ThreadLocal lastOperation = new ThreadLocal<>(); static { mtcMDCAdapter = new TtlMDCAdapter(); MDC.mdcAdapter = mtcMDCAdapter; } public static MDCAdapter getInstance() { return mtcMDCAdapter; } private Integer getAndSetLastOperation(int op) { Integer lastOp = lastOperation.get(); lastOperation.set(op); return lastOp; } private static boolean wasLastOpReadOrNull(Integer lastOp) { return lastOp == null || lastOp == MAP_COPY_OPERATION; } private Map duplicateAndInsertNewMap(Map oldMap) { Map newMap = Collections.synchronizedMap(new HashMap<>()); if (oldMap != null) { // we don't want the parent thread modifying oldMap while we are // iterating over it synchronized (oldMap) { newMap.putAll(oldMap); } } copyOnInheritThreadLocal.set(newMap); return newMap; } /** * 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. * * @throws IllegalArgumentException in case the "key" parameter is null */ @Override public void put(String key, String val) { if (key == null) { throw new IllegalArgumentException("key cannot be null"); } Map oldMap = copyOnInheritThreadLocal.get(); Integer lastOp = getAndSetLastOperation(WRITE_OPERATION); if (wasLastOpReadOrNull(lastOp) || oldMap == null) { Map newMap = duplicateAndInsertNewMap(oldMap); newMap.put(key, val); } else { oldMap.put(key, val); } } /** * Remove the the context identified by the key parameter. *

*/ @Override public void remove(String key) { if (key == null) { return; } Map oldMap = copyOnInheritThreadLocal.get(); if (oldMap == null) { return; } Integer lastOp = getAndSetLastOperation(WRITE_OPERATION); if (wasLastOpReadOrNull(lastOp)) { Map newMap = duplicateAndInsertNewMap(oldMap); newMap.remove(key); } else { oldMap.remove(key); } } /** * Clear all entries in the MDC. */ @Override public void clear() { lastOperation.set(WRITE_OPERATION); copyOnInheritThreadLocal.remove(); } /** * Get the context identified by the key parameter. *

*/ @Override public String get(String key) { final Map map = copyOnInheritThreadLocal.get(); if ((map != null) && (key != null)) { return map.get(key); } else { return null; } } /** * Get the current thread's MDC as a map. This method is intended to be used * internally. */ public Map getPropertyMap() { lastOperation.set(MAP_COPY_OPERATION); return copyOnInheritThreadLocal.get(); } /** * Returns the keys in the MDC as a {@link Set}. The returned value can be * null. */ public Set getKeys() { Map map = getPropertyMap(); if (map != null) { return map.keySet(); } else { return null; } } /** * Return a copy of the current thread's context map. Returned value may be * null. */ @Override public Map getCopyOfContextMap() { Map hashMap = copyOnInheritThreadLocal.get(); if (hashMap == null) { return null; } else { return new HashMap<>(hashMap); } } @Override public void setContextMap(Map contextMap) { lastOperation.set(WRITE_OPERATION); Map newMap = Collections.synchronizedMap(new HashMap<>()); newMap.putAll(contextMap); // the newMap replaces the old one for serialisation's sake copyOnInheritThreadLocal.set(newMap); } }