Spring Boot Slf4j MDC 实现全链路日志追踪

Spring Boot logback About 2,687 words

需求

请求进入容器后,注入特定的traceId,日志打印时能根据traceId关联到在ControllerServiceRepo层的日志。

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复制一份MDCHashMap,在子线程中使用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,912 · Posted: 2022-10-01

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb/LiteNote

扫描下方二维码关注公众号和小程序↓↓↓

扫描下方二维码关注公众号和小程序↓↓↓


Today On History
Browsing Refresh