项目中在多线程使用的地方,日志里的链路id(mdc类设置的)没法自动往子线程传递,需要手动设置进去
目前配置只支持spring提供的线程池ThreadPoolTaskExecutor使用
这边通过spring提供的装饰器拓展接口,将该类设置进去
public void setTaskDecorator(TaskDecorator taskDecorator) {
this.taskDecorator = taskDecorator;
}
/**
* @author :wangcheng
* @date2021/12/6
**/
@Component
public class MDCTaskDecorator implements TaskDecorator, BeanPostProcessor {
@Override
public Runnable decorate(Runnable runnable) {
Map<String, String> mdcContext = MDC.getCopyOfContextMap();
return () -> {
try {
if (mdcContext!=null) {
MDC.setContextMap(mdcContext);
}
runnable.run();
} finally {
MDC.clear();
}
};
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ThreadPoolTaskExecutor) {
((ThreadPoolTaskExecutor) bean).setTaskDecorator(this);
}
return bean;
}
}
有的老版本spring线程池没有提供该拓展接口,就要手动在包一层了
threadPoolTaskExecutor.execute(mdcTaskDecorator.decorate(()->xxxxx具体执行));
至于为什么子线程内部通过mdc.get(“xx”)方法获取不到值,可以进入源码看看
项目中使用logback日志,通过断点进入该put方法
public static void put(String key, String val) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException("key parameter cannot be null");
}
if (mdcAdapter == null) {
throw new IllegalStateException("MDCAdapter cannot be null. See also " + NULL_MDCA_URL);
}
mdcAdapter.put(key, val);
}
通过适配器 static MDCAdapter mdcAdapter;
进入put方法
public interface MDCAdapter {
public void put(String key, String val);
public String get(String key);
public void remove(String key);
public void clear();
public Map<String, String> getCopyOfContextMap();
public void setContextMap(Map<String, String> contextMap);
}
最终来到LogbackMDCAdapter
public class LogbackMDCAdapter implements MDCAdapter {
//省略其他。。。
final ThreadLocal<Map<String, String>> copyOnThreadLocal = new ThreadLocal<Map<String, String>>();
public void put(String key, String val) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
Map<String, String> oldMap = copyOnThreadLocal.get();
Integer lastOp = getAndSetLastOperation(WRITE_OPERATION);
if (wasLastOpReadOrNull(lastOp) || oldMap == null) {
Map<String, String> newMap = duplicateAndInsertNewMap(oldMap);
newMap.put(key, val);
} else {
oldMap.put(key, val);
}
}
}
可以看到最终put的是这个copyOnThreadLocal,无法多线程共享