slf4j打印traceid

我们在调试代码时会打很多日志,这些错综复杂的日志往往混杂在一起,很难筛选出某个请求链路的日志。我们就希望在每个请求到来时生成一个唯一的traceid,可以set到请求头信息中,打日志时带上就可以快速筛选请求链路的日志了。slf4j提供了这样的功能(MDC),slf4j用ThreadLocal来存储traceid。可以用拦截器、aop等方式使用,以下是拦截器的示例:

  • 首先注册一个拦截器,在preHandle方法中调用MDC.put()方法,保存随机生成的id。在afterCompletion方法中删除。
public class TraceInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        MDC.put("trace", UUID.randomUUID().toString());
        return super.preHandle(request, response, handler);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        MDC.remove("trace");
        super.afterCompletion(request, response, handler, ex);
    }
}
<mvc:interceptors>
        <bean class="com.xxx.interceptor.TraceInterceptor"/>
</mvc:interceptors>
  • 在logback.xml中添加%X{name}
<property name="defaultPattern"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %X{trace} %t \\(%F:%L\\) - %msg%n"/>

然后就可以在日志中看到每个请求的traceid了,可以通过cat grep查看请求链路的日志

2020-09-04 15:41:59.386 INFO  159920597.704 b5491258-6b41-4cf9-8980-3bf336713afc 10.58.75.46:60454 1440771206 http-nio-8085-exec-10 (XXX.java:432) - [onf] config INITIALIZE, key:dot, version:-1, stage:null

 

源码分析:

MDC通过mdcAdapter变量进行操作

public class MDC {
    static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA";
    static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder";
    static MDCAdapter mdcAdapter;


    public static void put(String key, String val) throws IllegalArgumentException {
        if (key == null) {
            throw new IllegalArgumentException("key parameter cannot be null");
        } else if (mdcAdapter == null) {
            throw new IllegalStateException("MDCAdapter cannot be null. See also http://www.slf4j.org/codes.html#null_MDCA");
        } else {
            mdcAdapter.put(key, val);
        }
    }

    static {
        try {
            mdcAdapter = bwCompatibleGetMDCAdapterFromBinder();
        } catch (NoClassDefFoundError var2) {
            mdcAdapter = new NOPMDCAdapter();
            String msg = var2.getMessage();
            if (msg == null || !msg.contains("StaticMDCBinder")) {
                throw var2;
            }

            Util.report("Failed to load class \"org.slf4j.impl.StaticMDCBinder\".");
            Util.report("Defaulting to no-operation MDCAdapter implementation.");
            Util.report("See http://www.slf4j.org/codes.html#no_static_mdc_binder for further details.");
        } catch (Exception var3) {
            Util.report("MDC binding unsuccessful.", var3);
        }

    }
    。。。

}

可以看到mdcAdapter = bwCompatibleGetMDCAdapterFromBinder();

private static MDCAdapter bwCompatibleGetMDCAdapterFromBinder() throws NoClassDefFoundError {
        try {
            return StaticMDCBinder.getSingleton().getMDCA();
        } catch (NoSuchMethodError var1) {
            return StaticMDCBinder.SINGLETON.getMDCA();
        }
    }

 StaticMDCBinder类

public class StaticMDCBinder {
    public static final StaticMDCBinder SINGLETON = new StaticMDCBinder();

    private StaticMDCBinder() {
    }

    public MDCAdapter getMDCA() {
        return new LogbackMDCAdapter();
    }

    public String getMDCAdapterClassStr() {
        return LogbackMDCAdapter.class.getName();
    }
}

LogbackMDCAdapter类及put方法,可以看到有一个ThreadLocal变量

public class LogbackMDCAdapter implements MDCAdapter {
    final ThreadLocal<Map<String, String>> copyOnThreadLocal = new ThreadLocal();
    private static final int WRITE_OPERATION = 1;
    private static final int MAP_COPY_OPERATION = 2;
    final ThreadLocal<Integer> lastOperation = new ThreadLocal();


    public void put(String key, String val) throws IllegalArgumentException {
        if (key == null) {
            throw new IllegalArgumentException("key cannot be null");
        } else {
            Map<String, String> oldMap = (Map)this.copyOnThreadLocal.get();
            Integer lastOp = this.getAndSetLastOperation(1);
            if (!this.wasLastOpReadOrNull(lastOp) && oldMap != null) {
                oldMap.put(key, val);
            } else {
                Map<String, String> newMap = this.duplicateAndInsertNewMap(oldMap);
                newMap.put(key, val);
            }

        }
    }

    。。。

   
}

还一个BasicMDCAdapter类暂时不知道怎么使用的,内部维护了一个InheritableThreadLocal的变量。只知道是为了解决这个问题:ThreadLocal只保证在同一个线程间共享变量,也就是说如果这个线程起了一个新线程,那么新线程是不会得到父线程的变量信息的。
 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值