Springboot-用MDC生成链路追踪Txid

1,151 阅读2分钟

什么是MDC(Mapped Diagnostic Contexts)

MDC本质上是一个Map,基于线程用于存储任意键值对,通常是调用的上下文(context)信息,
由应用代码存储键值对,并由日志框架应用到日志消息中。如系统接收到请求时,
系统会把请求ID,请求发起方ID,请求发起方IP地址和请求参数放在MDC。
目前支持MDC的日志框架包括log4j和logback。

MDC是线程独立、线程安全的,通常无论是HTTP还是RPC请求,都是在各自独立的线程中完成的.

MDC能为我们做什么

大部分我们的程序调用情况都比较复杂,所以在很多地方都会打印方法的入参及出参日志。
但当请求一多的时候我们无法区分,哪条日志是同一次请求的。
MDC这时候出场,它可以 把一次请求中所有请求日志都找出来。

logback.xml如何使用MDC



<property name="LOG_PATTERN" value="[timestamp=%d{yyyy-MM-dd HH:mm:ss.SSS}]

[level=%p] [tx_id=%X{traceId}] [app_id=${applicationName}] [%t][%L][%c.%M] %m%n"/>

%X{traceId}就是在引用MDC中的内容,{key}可以自己通过代码设置。


其他地方使用也大致可以理解成两步:
1:定义日志的格式,其中%X{}代表去MDC取值
2:通过aop或者拦截器等方式在初始入口设置MDC的值

Springboot-用MDC生成链路追踪Txid

定义TraceIdUtil的常量

public class TraceIdUtil {

    public static final String TRACE_ID = "traceId";


    /**
     * 可以根据自己的需求生成不同个是tx_id
     *
     * @return
     */
    public static String getTxID() {
        return UUID.randomUUID().toString().replace("-", "");
    }


    public static void setTraceId(String traceId) {
        if (StringUtils.isNoneBlank(traceId)) {
            MDC.put(TRACE_ID, traceId);
        }
    }

    public static void clearTraceId() {
        MDC.clear();
    }


}
Filter拦截
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.AbstractRequestLoggingFilter;
import traceid.request.logging.utill.TraceIdUtil;

import javax.servlet.http.HttpServletRequest;

/**
 * Created by  on 2021/12/24.
 */
@Component
public class TraceIdFilter extends AbstractRequestLoggingFilter {

    /**
     * 方法前拦截
     *
     * @param httpServletRequest
     * @param s
     */
    @Override
    protected void beforeRequest(HttpServletRequest httpServletRequest, String s) {
    
        //如果在请求的源头找不到,就生成一个放到header
        String txId = httpServletRequest.getHeader(TraceIdUtil.TRACE_ID);
        if (StringUtils.isEmpty(txId)) {
            TraceIdUtil.setTraceId(TraceIdUtil.getTxID());
        } else {
            TraceIdUtil.setTraceId(txId);
        }

    }

    @Override
    protected void afterRequest(HttpServletRequest httpServletRequest, String s) {
        TraceIdUtil.clearTraceId();
    }
}


测试类构造
@RestController
public class TestController {

    private Logger logger = LoggerFactory.getLogger(getClass());


    @RequestMapping(value = "/test")
    public String test(String name) {
        logger.info("get test,time={},name={}", System.currentTimeMillis(), name);
        testLog();
        return "success";
    }

    private void testLog() {
        logger.info("testlog");
    }
}

最终效果

image.png