slf4j打印traceid

2024-03-17 23:32
文章标签 打印 slf4j traceid

本文主要是介绍slf4j打印traceid,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

  • 首先注册一个拦截器,在preHandle方法中调用MDC.put()方法,保存随机生成的id。在afterCompletion方法中删除。
public class TraceInterceptor extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {MDC.put("trace", UUID.randomUUID().toString());return super.preHandle(request, response, handler);}@Overridepublic 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只保证在同一个线程间共享变量,也就是说如果这个线程起了一个新线程,那么新线程是不会得到父线程的变量信息的。
 

这篇关于slf4j打印traceid的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/820537

相关文章

springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法

《springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法》:本文主要介绍springboot整合阿里云百炼DeepSeek实现sse流式打印,本文给大家介绍的非常详细,对大... 目录1.开通阿里云百炼,获取到key2.新建SpringBoot项目3.工具类4.启动类5.测试类6.测

SpringBoot日志配置SLF4J和Logback的方法实现

《SpringBoot日志配置SLF4J和Logback的方法实现》日志记录是不可或缺的一部分,本文主要介绍了SpringBoot日志配置SLF4J和Logback的方法实现,文中通过示例代码介绍的非... 目录一、前言二、案例一:初识日志三、案例二:使用Lombok输出日志四、案例三:配置Logback一

SpringBoot项目注入 traceId 追踪整个请求的日志链路(过程详解)

《SpringBoot项目注入traceId追踪整个请求的日志链路(过程详解)》本文介绍了如何在单体SpringBoot项目中通过手动实现过滤器或拦截器来注入traceId,以追踪整个请求的日志链... SpringBoot项目注入 traceId 来追踪整个请求的日志链路,有了 traceId, 我们在排

异步线程traceId如何实现传递

《异步线程traceId如何实现传递》文章介绍了如何在异步请求中传递traceId,通过重写ThreadPoolTaskExecutor的方法和实现TaskDecorator接口来增强线程池,确保异步... 目录前言重写ThreadPoolTaskExecutor中方法线程池增强总结前言在日常问题排查中,

SpringBoot如何使用TraceId日志链路追踪

《SpringBoot如何使用TraceId日志链路追踪》文章介绍了如何使用TraceId进行日志链路追踪,通过在日志中添加TraceId关键字,可以将同一次业务调用链上的日志串起来,本文通过实例代码... 目录项目场景:实现步骤1、pom.XML 依赖2、整合logback,打印日志,logback-sp

使用@Slf4j注解,log.info()无法使用问题

《使用@Slf4j注解,log.info()无法使用问题》在使用Lombok的@Slf4j注解打印日志时遇到问题,通过降低Lombok版本(从1.18.x降至1.16.10)解决了问题... 目录@Slf4androidj注解,log.info()无法使用问题最后解决总结@Slf4j注解,log.info(

多数据源的事务处理总是打印很多无用的log日志

之前做了一个项目,需要用到多数据源以及事务处理,在使用事务处理,服务器总是打印很多关于事务处理的log日志(com.atomikos.logging.Slf4jLogger),但是我们根本不会用到这些log日志,反而使得查询一些有用的log日志变得困难。那要如何屏蔽这些log日志呢? 之前的项目是提高项目打印log日志的级别,后来觉得这样治标不治本。 现在有一个更好的方法: 我使用的是log

fastreport打印trichedit分页问题的解决

用fastreport来打印richedit里面的内容。刚开始放一个frxrichview组件到报表上,然后在 var str: TMemoryStream; begin    begin      str:= TMemoryStream.Create;      CurrRichRecord.richedit.Lines.SaveToStream(str);      str.Posit

模具要不要建设3D打印中心

随着3D打印技术的日益成熟与广泛应用,模具企业迎来了自建3D打印中心的热潮。这一举措不仅为企业带来了前所未有的发展机遇,同时也伴随着一系列需要克服的挑战,如何看待企业引进增材制造,小编为您全面分析。 机遇篇: 加速产品创新:3D打印技术如同一把钥匙,为模具企业解锁了快速迭代产品设计的可能。企业能够迅速将创意转化为实体模型,缩短产品从设计到市场的周期,抢占市场先机。 强化定制化服务:面

Java项目中,配置打印 JDBC 日志的几种方法

在 IDEA 项目中,如果你想打印 JDBC 日志,可以通过配置日志框架(如 Logback 或 Log4j)来实现。Spring Boot 使用的默认日志框架是 Logback,你可以通过在 application.yml 文件中配置日志级别来打印 JDBC 日志。 方法 1: 使用 application.yml 配置 JDBC 日志 logging:level:# 显示 SQL 语句co