SpringBoot中日志TraceId追踪

日志门面 定义一套日志接口(规范),常见的有slf4j,common-logging.
日志系统(具体的实现),常见的有log4j2,log-back,log4j等
SpringBoot中默认是slf4j+log-back组合,不需要额外引入日志依赖

日志配置

<configuration debug="false">
    <!-- 日志的输出策略(例如 日志输出格式,日志时间区间覆盖,文件大小达到阈值新建文件等)控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{HH:mm:ss} [%thread][%X{traceId}] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- logger定义指定包下日志的输出级别 -->
    <logger name="com.gyp.controller" level="TRACE" >
        <appender-ref ref="STDOUT"/>
    </logger>
    <!-- root全局日志输出级别 -->
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

可以查看大佬的介绍https://www.cnblogs.com/warking/p/5710303.html

日志输出时如何注入自定义key-value

自定义的日志输出信息。通过MDC这个类存储实现,底层用的是ThreadLocal类。
例如上述的traceId,可以通过MDC.put()方法进行注入。log.info()输出日志时,就是自动注入traceId字段的值。

手动埋点-日志路径追踪

日志追踪,可以理解为在每条日志信息上,附加一条UUID。 UUID相同的日志就是一条调用链上的
在web应用中,一次接口调用可以理解为一条任务执行链
如果是rpc feign调用,在请求头中放入traceId,服务端从请求头中取出即可。

上述方案只适用于同一个线程下(ThreadLocal线程之间隔离了,每个Thread中的ThreadLocalMap保存着ThreadLocal一个副本)。如果执行某个方法需要开启其他线程情况下,此时其他线程的MDC无法获取到traceId值。
可以通过JDK中的InheritableThreadLocal对象存储一份traceId,父线程在创建子线程时会将ThreadLocalMap中的值Copy到子线程中。此时子线程就会得到traceId值,然后注入到MDC中就可以了

InheritableThreadLocal只会在子线程创建时copy,如果线程池中的线程复用就会出现问题了。可以使用TransmittableThreadLocal 解决这个问题,alibaba开发的工具类,其实现了InheritableThreadLocal类。

TransmittableThreadLocal也提供了前置和后置钩子函数。
钩子函数触发时机是在执行Runnable或者Callable接口实现类(还有一步必须通过TtlRunnable,TtlCallable进行二次封装),执行前后触发。 这种方式无需手动把traceId注入到MDC中。
第二种方式是使用TtlExecutors对已有的线程池进行封装

//定义工具类
public class ThreadHolder {
		//创建全局对象
        private static TransmittableThreadLocal<String> transmittableThreadLocal = new TransmittableThreadLocal() {
            @Override
            protected void beforeExecute() {
                MDC.put("traceId", transmittableThreadLocal.get());
                System.err.println("执行beforeExecute--------------");
            }
            @Override
            protected void afterExecute() {
                MDC.clear();
                System.err.println("执行afterExecute=-------------");
            }

        };
        public static void setTraceId(String value) {
            transmittableThreadLocal.set(value);
            MDC.put("traceId", value);
        }
        public static String getTraceId() {
            return transmittableThreadLocal.get();
        }
        public static void remove() {
            transmittableThreadLocal.remove();
        }
}


 @GetMapping("/log1")
 public String test1(){
     String id = UUID.randomUUID().toString();
     ThreadHolder.setTraceId(id);
     threadPool.submit(getTTlRunnable(()->log.error("线程池中的日志")));
     log.error("父线程日志");
     return id;
     /**日志打印结果
      * 执行beforeExecute--------------
      * 15:47:36 [http-nio-8080-exec-1][15cf48d2-40f2-46ff-a0c7-2ca88f0ec4ff] ERROR com.gyp.controller.TestController - 父线程日志
      * 15:47:36 [pool-1-thread-1][15cf48d2-40f2-46ff-a0c7-2ca88f0ec4ff] ERROR com.gyp.controller.TestController - 线程池中的日志
      * 执行afterExecute=-------------
      */
 }
//这种方式无需手动注入到MDC
 public TtlRunnable getTTlRunnable(Runnable runnable){
     return TtlRunnable.get(runnable);
 }

自动埋点-日志路径追踪

无需程序员改动或添加逻辑代码,通过jvm编译时,自动生成字节码注入到代码中。
常见的有SkyWalking,阿里云的sls。

感谢你的浏览,
以上是博主的一些心得,
有错误请指出,谢谢!

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值