MDC是SLF4J中的一个类,通过MDC我们可以很方便的实现同一个线程内(包括父线程和子线程之间)的日志的追踪,对于一个请求能够很容易的根据traceId这样的值很快的找到该线程包括子线程所经历的全部的日志,轻松的把程序中的逻辑串联起来,通过一个例子来说明。
目录
MDC使用
引入相关依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
同时需要在logback.xml中添加对应的appender的pattern,主要是这个[traceId = %X{traceId}]
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd hh:mm:ss} [%thread] [traceId = %X{traceId}] [%logger{32}] - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="console"/>
</root>
</configuration>
用法一
在一个线程中的使用,代码如下:
@ProviderAnnotation(value = "sync")
public String sync(DTO dto) {
MDC.put("traceId", dto.getTraceId);
LogConstant.ACC_LOG.info("begin");
return "hello";
}
主要的代码是
MDC.put("traceId", dto.getTraceId);
需要注意的是和使用ThreadLocal一样,一定要记得clear,事实上这玩意的底层就是ThreadLocal,只是slf4j自己封装了而已,我此处的在aop中统一进行的clear操作。
finally {
MDC.clear();
}
效果是:
[pool-12-thread-2] [traceId = aaa] .......
用法二
使用上面的方法发现在我主线程中开启异步的线程或者使用线程池时没有将该traceId打印出来,我们需要相关的代码做如下调整:
ExecutorService executor = Executors.newSingleThreadExecutor();
Map<String, String> context = MDC.getCopyOfContextMap();
executor.execute(()->{
try{
MDC.setContextMap(context);
LogConstant.ACC_LOG.info("xxx ");
} finally {
MDC.clear();
}
});
关键代码是:
Map<String, String> context = MDC.getCopyOfContextMap();
这个一定要在run之前先获取一下,否则可能导致下面取不到值。
MDC.setContextMap(context);
同时别忘记clear的操作,打印效果和上面一致。