Spring Boot Slf4j MDC 实现全链路日志追踪
Spring Boot logback About 2,687 words需求
请求进入容器后,注入特定的traceId
,日志打印时能根据traceId
关联到在Controller
、Service
、Repo
层的日志。
Slf4j MDC
Slf4j
中的MDC
底层使用了ThreadLocal
实现方法间传递变量。
定义 logback 配置文件
%X{traceId:-}
:使用%X
获取定义的traceId
,如果没有赋值,则使用空字符。(配置文件中的[]
可去掉,只是区分显示而已,无其他作用。)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<springProperty name="appName" source="spring.application.name"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] [%level] [%class : %method : %line] [%X{traceId:-}] [${appName}] : %message %n
</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
<logger name="com.example" level="DEBUG"/>
</configuration>
定义拦截器
使用MDC.put()
方法存储变量,使用MDC.remove()
方法移动变量。
@Component
public class TraceIdInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
MDC.put("traceId", UUID.randomUUID().toString());
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
MDC.remove("traceId");
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
配置拦截器
@Configuration
public class WebAppConfig implements WebMvcConfigurer {
@Resource
private TraceIdInterceptor traceInterceptor;
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(traceInterceptor).addPathPatterns("/**");
}
}
子线程问题
方法中使用了异步线程,需使用CopyOfContextMap
复制一份MDC
的HashMap
,在子线程中使用MDC.setContextMap
设置这份拷贝的数据,就能使用异步传递了。
注意子线程中是拷贝的数据,如果在线程池中使用,需要remove
。
public String sayHello() {
log.info("service");
Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
log.info("contextMap#{}", copyOfContextMap);
try {
return helloRepo.sayHello();
} finally {
new Thread(() -> {
try {
MDC.setContextMap(copyOfContextMap);
Thread.sleep(100);
log.info("sub thread before clear#{}", MDC.getCopyOfContextMap());
} catch (Exception ignore) {
} finally {
MDC.clear();
log.info("sub thread after clear#{}", MDC.getCopyOfContextMap());
}
}).start();
}
}
Views: 1,950 · Posted: 2022-10-01
————        END        ————
Give me a Star, Thanks:)
https://github.com/fendoudebb/LiteNote扫描下方二维码关注公众号和小程序↓↓↓
Loading...