ThreadLocal项目实战-TraceId日志

809 阅读1分钟

在日志中加入traceId,可以追踪链路,在分布式系统中十分常用,我们基于org.slf4j.MDC来实现,其原理就是用到ThreadLocal。

在请求上加入TRACE_ID,也可以直接在请求头上赋值。利用拦截器,拦截处理:

public class TestTraceInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //自身内部MDC
        MDC.put(Constants.TRACE_ID, traceId());
        return super.preHandle(request, response, handler);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        super.afterCompletion(request, response, handler, ex);
        MDC.clear();
    }


    public static String traceId() {
        return UUID.randomUUID().toString().toUpperCase();
    }
}

在返回日志加入traceId:结合ResponseBodyAdvice使用可以对返回参数进行增强,额外封装参数

@ControllerAdvice
public class ResponseVOFilterAdvice implements ResponseBodyAdvice<Object> {

private static final Logger logger =
        LoggerFactory.getLogger(ResponseVOFilterAdvice.class);

@Override
public boolean supports(MethodParameter returnType,
                        Class<? extends HttpMessageConverter<?>> converterType) {
    return true;
}

@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
                              MediaType selectedContentType,
                              Class<? extends HttpMessageConverter<?>> selectedConverterType,
                              ServerHttpRequest request,
                              ServerHttpResponse response) {
    if (body instanceof BaseDataVO) {
        BaseDataVO dataVO = (BaseDataVO) body;
        dataVO.setTraceId(MDC.get(Constants.TRACE_ID));
        logger.info("ResBody:{}", JacksonUtils.toJson(dataVO));
    }
    return body;
}

日志打印补充traceId打印:

<SensitivePatternLayout pattern="%d{yyyy/MM/dd HH:mm:ss.SSS} %X{TRACE-ID} %t [%p] %c{1} (%F:%L) %msg%n">

异步线程tracecId日志打印:

public class MdcTaskDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        Map<String, String> contextMap = MDC.getCopyOfContextMap();
        return () -> {
            try {
                MDC.setContextMap(contextMap);
                runnable.run();
            } finally {
                MDC.clear();
            }
        };
    }
}

在创建线程池上加入装饰器:

executor.setTaskDecorator(new MdcTaskDecorator());

打印日志效果: 2020/07/08 18:56:00.278 2E1D7F0B-C9A4-482A-9C72-2B393676A54B Thread-94 [INFO] XXXXXXX