怎么把CAT客户端的RootMessageId记录到每条日志中?

2022-10-13,,,,

什么是rootmessageid?

为了理解rootmessageid先简单介绍一下cat的数据结构设计。cat客户端会将所有消息都封装为一个完整的消息树(messagetree),消息树可能包括transaction、event、heartbeat、metric等类型的消息。具体如下:

  • transaction:适合记录跨越系统边界的程序访问行为,比如远程调用,数据库调用,也适合执行时间较长的业务逻辑监控,transaction用来记录一段代码的执行时间和次数
  • event:用来记录一件事发生的次数,比如记录系统异常,它和transaction相比缺少了时间的统计,开销比transaction要小
  • heartbeat:表示程序内定期产生的统计信息, 如cpu利用率, 内存利用率, 连接池状态, 系统负载等
  • metric:用于记录业务指标、指标可能包含对一个指标记录次数、记录平均值、记录总和,业务指标最低统计粒度为1分钟

欢迎关注微信公众号:万猫学社,每周一分享java技术干货。

其中,transaction类型的消息可作为消息树节点,而其他消息只可作为消息树的叶子节点,也就是transaction是一个可嵌套的递归结构。比如:

消息树的每一节点都有一个属性messageid,用来唯一表示节点本身,其构成为:{domain}-{ip}-{timestamp}-{自增index}。另外还有两个属性,分别是parentmessageid, rootmessageid。parentmessageid表示父节点的messageid;rootmessageid则表示整个消息树的根节点的messageid。这两个属性在之后cat的调用链分析与分布式调用链分析中发挥了关键作用。

欢迎关注微信公众号:万猫学社,每周一分享java技术干货。

为什么在日志中记录?

根据rootmessageid可以追踪某一个请求的整个分布式调用链,结合每一条日志快速定位耗费性能的症结,做针对性的性能优化。更加方便地做性能优化,特别是tp95、tp99等指标。

遇到偶尔发生的bug,是最让人头疼的,只有先从日志中找线索,但是在海量的日志中找到出现bug的那一个请求是很困难的。有了上游api提供的rootmessageid,就可以快速过滤出那次请求的所有日志, 更快速更方便地定位线上bug。

欢迎关注微信公众号:万猫学社,每周一分享java技术干货。

在日志的什么地方记录?

当然是每一句日志上都记录rootmessageid了。有的同学会说,这日志也记录的太多了。当发现线上问题无法定位时,你就会狠日志太少了。其实记录日志不怕多,就怕不全。现在硬盘很便宜了,搞个几t没有问题,另外还可以设置日志清理策略。

欢迎关注微信公众号:万猫学社,每周一分享java技术干货。

怎么记录到日志中?

前面说了那么多,终于到了今天的压轴大戏了。实现记录到日志有很多种方式,这里使用的是mdc(mapped diagnostic contexts)。顾名思义,其目的是为了便于我们诊断线上问题而出现的方法工具类,目前我们经常使用的logback和log4j都是支持的。

欢迎关注微信公众号:万猫学社,每周一分享java技术干货。

只需要在每个请求的入口调用mdc.put方法,把rootmessageid赋值进去就可以了,是不是很简单?示例代码:

//在filter里,从header里获取上下文信息,包括messageid、parentmessageid、rootmessageid
catcontext catcontext = new catcontext();
catcontext.addproperty(cat.context.root, request.getheader(catconstants.cat_http_header_root_message_id));
catcontext.addproperty(cat.context.parent, request.getheader(catconstants.cat_http_header_parent_message_id));
catcontext.addproperty(cat.context.child, request.getheader(catconstants.cat_http_header_child_message_id));
if (catcontext.getproperty(cat.context.root) == null) {
    //如果调用链的顶端,没有上下文信息,需要生成上下文信息
    cat.logremotecallclient(catcontext);
} else {
    cat.logremotecallserver(catcontext);
}
mdc.put("traceid", catcontext.getproperty(cat.context.root));

如果你还不知道怎么集成cat调用链,可以看看之前的《springboot集成cat调用链实例》

然后,在设置日志输出格式的配置文件里增加[%x{traceid}]

logback的xml配置示例:

<?xml version="1.0" encoding="utf-8" ?>
<configuration scan="true">
    <appender name="console" class="ch.qos.logback.core.consoleappender">
        <layout class="ch.qos.logback.classic.patternlayout">
            <pattern>[%d{yyyy-mm-dd hh:mm:ss.sss}] [%thread] [%x{traceid}] [%-5level] [%-40.36logger{40}:%-4.4line] - %msg%n</pattern>
        </layout>
    </appender>

    <root level="info">
        <appender-ref ref="console" />
    </root>
</configuration>

log4j的properties配置示例:

log4j.rootcategory=info,stdout,info,error
log4j.rootlooger=warn,stdout,info,error
log4j.appender.stdout=org.apache.log4j.consoleappender
log4j.appender.stdout.layout=org.apache.log4j.patternlayout
log4j.appender.stdout.layout.conversionpattern=%d{yyyy-mm-dd hh:mm:ss,sss} [%-5p] [%thread] [%x{traceid}]  method:%l - %m%n

欢迎关注微信公众号:万猫学社,每周一分享java技术干货。

《怎么把CAT客户端的RootMessageId记录到每条日志中?.doc》

下载本文的Word格式文档,以方便收藏与打印。