侧边栏壁纸
  • 累计撰写 3 篇文章
  • 累计创建 9 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

日志添加TraceId

code2fun
2023-04-14 / 0 评论 / 0 点赞 / 525 阅读 / 840 字

背景

项目环境没有接入skywalking等apm监控,也没有日志平台查看日志,这两天代码开发,只能通过线上机器日志去查询。如果日志刷屏、方法链路长,通过grep无法查询到整个请求的链路,严重影响效率。
文章目标:

  1. 了解主流APM日志插件集成
  2. 实现自己的日志插件记录traceId
  3. todo,细节的优化

日志插件集成

以skywalking 为例,如何在日志中,将链路traceId输出。

Print trace ID in your logs

  • Dependency the toolkit, such as using maven or gradle
<dependency>
      <groupId>org.apache.skywalking</groupId>
      <artifactId>apm-toolkit-log4j-2.x</artifactId>
      <version>{project.release.version}</version>
   </dependency>
  • Config the [%traceId] pattern in your log4j2.xml
 <Appenders>
       <Console name="Console" target="SYSTEM_OUT">
          <PatternLayout pattern="%d [%traceId] %-5p %c{1}:%L - %m%n"/>
       </Console>
    </Appenders>
    ```

 通过上面两步,我们就可以将traceId 集成到我们的打印日志中。其他APM 如pinpoint等,基本一致。

 

 看下代码,发现很简单,就两个类

 ```java
 @Plugin(
     name = "TraceIdConverter",
     category = "Converter"
 )
 @ConverterKeys({"traceId"})
 public class TraceIdConverter extends LogEventPatternConverter {
     protected TraceIdConverter(String name, String style) {
         super(name, style);
     }
 
     public static TraceIdConverter newInstance(String[] options) {
         return new TraceIdConverter("traceId", "traceId");
     }
 
     public void format(LogEvent event, StringBuilder toAppendTo) {
         Log4j2OutputAppender.append(toAppendTo);
     }
 }
public class Log4j2OutputAppender {
    public Log4j2OutputAppender() {
    }

    public static void append(StringBuilder toAppendTo) {
        toAppendTo.append("TID: N/A");
    }
}

代码很简单,通过log4j插件,实现一套获取traceId 的机制,log4j再打印日志的时候,会将日志内容中拼接上对应标识位。

自己动手实现

看了skywalking的代码,我们其实也可以动手实现一个自己的日志集成traceId组件。只是我们要在Log4j2OutputAppender中自己实现一套获取traceId的机制。

详细代码可以看git代码

public class YmLogAppender {

    //todo remove
    private  static  final ThreadLocal<String> threadLocal = new ThreadLocal<>();
    public YmLogAppender() {
    }
    public static void append(StringBuilder toAppendTo) {
        String tid = threadLocal.get();
        if (StringUtils.isBlank(tid)){
            tid= UUID.randomUUID().toString();
            threadLocal.set(tid);
        }
        toAppendTo.append("TID:"+tid);
    }
}

日志文件中,加入[%traceId]即可

<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p][%traceId] - %l - %m%n"/>

看下效果

通过grep tid 就可以将整个请求的链路的日志全都打印出来。

后记

简单的demo已经完成,并能提升一定的开发效率。但是细节上还有可以改进的点。

  1. 使用threadLocal存储上下文的日志信息,得考虑跨线程请情况

    可以通过transmittable-thread-local 完成父子线程上下文的透传

  2. threadLocal WeakReference问题

    添加拦截器,拦截应用入口方法,在方法结束时,完成remove操作

  3. 跨应用透传

    在RPC上下文中,存储当前请求的tid.通过RPC拦截器,获取上下文,进行传递

题外话,现在一些云厂商的链路追踪插件,通过javaagent集成,可以无编码完成traceId的打印,以及请求traceId 的接口返回,功能更加强大。但是通过查看skywalking 的代码,我们可以清晰了解实现的思路,后续我们可以自定义添加标识字段到我们的日志中,方便查看。

0
博主关闭了所有页面的评论