Log子线程日志
前言
Github:https://github.com/HealerJean
一、MDC
介绍
MDC
(Mapped Diagnostic Context,映射调试上下文)是log4j
、logback
及log4j2
提供的一种方便在多线程条件下记录日志的功能。·MDC· 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对。MDC
中包含的内容可以被同一线程中执行的代码所访问。普通的使用方法在
Log
那篇文章中有,下面主要还是进攻
二、子线程日志打印丢失 traceId
子线程在打印日志的过程中
traceId
将丢失,解决方式为重写线程池,对于直接new
创建线程的情况不考略【实际应用中应该避免这种用法】,重写线程池无非是对任务进行一次封装。
1、MDC
封装线程工具
public class ThreadMdcUtil {
public static final String TRACE_ID = "REQ_UID";
public static final String SON_ID = "SON_UID";
public static void setTraceIdIfAbsent() {
if (MDC.get(TRACE_ID) == null) {
MDC.put(TRACE_ID, UUID.randomUUID().toString().replace("-", ""));
}
if (MDC.get(SON_ID) == null) {
MDC.put(SON_ID, UUID.randomUUID().toString().replace("-", ""));
}
}
/**
* 封装线程任务,在执行的时候放入MDC,执行结束删除MDC
* @param callable callable 线程任务
* @param context MDC属性
* @param <T>
* @return
*/
public static <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> context) {
return () -> {
if (context == null) {
MDC.clear();
} else {
MDC.setContextMap(context);
}
setTraceIdIfAbsent();
try {
return callable.call();
} finally {
MDC.clear();
}
};
}
/**
* 封装线程任务,在执行的时候放入MDC,执行结束删除MDC
* @param runnable Runnable 线程任务
* @param context MDC属性
* @return
*/
public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {
return () -> {
// 断当前线程对应MDC的Map是否存在,存在则设置
if (context == null) {
MDC.clear();
} else {
MDC.setContextMap(context);
}
setTraceIdIfAbsent();
try {
runnable.run();
} finally {
MDC.clear();
}
};
}
}
2、线程池wrapper
public class ThreadPoolExecutorMdcWrapper extends ThreadPoolExecutor {
public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
@Override
public void execute(Runnable task) {
super.execute(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
@Override
public <T> Future<T> submit(Runnable task, T result) {
return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()), result);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
@Override
public Future<?> submit(Runnable task) {
return super.submit(ThreadMdcUtil.wrap(task, MDC.getCopyOfContextMap()));
}
}
3、log4j2.xml
配置
%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level -[%-32X{REQ_UID}] %-32X{SON_UID} - %msg%xEx %logger{36}.%M[%L]%n
4、测试
@Slf4j
public class TestMain2_TraceId {
public static final String TRACE_ID = "REQ_UID";
@Test
public void threadpool() throws InterruptedException {
MDC.put(TRACE_ID, UUID.randomUUID().toString().replace("-", ""));
log.info("[TestMain2_TraceId#threadpool] 主线程开始");
ThreadPoolExecutorMdcWrapper threadPoolExecutorMdcWrapper = new ThreadPoolExecutorMdcWrapper(
10, 300, 30000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(10));
threadPoolExecutorMdcWrapper.submit(()->{
log.info("[TestMain2_TraceId#threadpool] 线程池任务1 start ");
log.info("[TestMain2_TraceId#test] 线程池任务1 end ");
});
threadPoolExecutorMdcWrapper.submit(()->{
log.info("[TestMain2_TraceId#threadpool] 线程池任务2 start ");
log.info("[TestMain2_TraceId#test] 线程池任务2 end ");
});
log.info("[TestMain2_TraceId#threadpool] 主线程结束");
Thread.sleep(3000);
MDC.remove(TRACE_ID);
}
}