Java应用日志如何与Jaeger的trace关联
欢迎访问我的GitHub
https://github.com/zq2599/blog_demos
内容:所有原创文章分类汇总及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;
本篇概览
-
经过《Jaeger开发入门(java版)》的实战,相信您已经能将自己的应用接入Jaeger,并用来跟踪定位问题了,本文将介绍Jaeger一个小巧而强大的辅助功能,用少量改动大幅度提升定位问题的便利性:将业务日志与Jaeger的trace关联
-
在正式开始前,咱们先来看一个具体的问题:
- 一次web请求可能有多条业务日志(log4j或者logback配置的那种),这和您写代码执行<font color="blue">log.info</font>的次数有关,假设有10条,那么十次请求就有一百条业务日志;
- 通过jaeger发现这十次请求中有一次耗时特别长,想定位一下具体原因,现在问题来了:一共有100条业务日志,到底哪些是和Jaeger中耗时长的那一次请求有关?
-
您可能会说:有些业务特征如user-id,咱们可以写入span的tag或者log中,这样通过span查到user-id,再去日志中查找含有此user-id的日志即可,这样确实可以,但未必每条日志都有user-id,所以并非最佳方式
-
好在Jaeger官方给出了一种简单有效的方案:基于MDC,Jaeger的SDK在日志中注入trace相关的变量
关于MDC
-
关于sl4j的MDC不是本篇的重点,因此只把本篇用到的特性简单说说即可,经验丰富的您如果对MDC已经了解,请跳过此节
-
在sl4j的配置文件中可以配置日志的格式,例如logback的配置文件如下,可见模板中新增了一段内容<font color="blue">[user-id=%X{user-id}]</font>:
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> <encoder> <!--%logger{10}表示类名过长时会自动缩写--> <pattern>%d{HH:mm:ss} [%thread] %-5level %logger{10} [user-id=%X{user-id}] %msg%n</pattern> <charset>utf-8</charset> </encoder> </appender>
- 再来看一段日志的代码,先调用<font color="blue">MDC.put</font>方法将一个键值对写入当前线程的诊断上下文map(diagnostic context map),键名和上面的模板中配置的<font color="red">%X{user-id}</font>一模一样:
@GetMapping("/test") public void test() { MDC.put("user-id", "user-" + System.currentTimeMillis()); log.info("this is test request"); }
- 现在把代码运行起来,打印日志看看,如下所示,之前模板中配置的<font color="red">%X{user-id}</font>已被替换成了<font color="blue">user-1632122267618</font>,就是代码中<font color="green">MDC.put</font>设置的值:
15:17:47 [http-nio-18081-exec-6] INFO c.b.j.c.c.HelloConsumerController [user-id=user-1632122267618] this is test request
-
以上就是MDC的基本功能:对日志模板中的变量进行填充,填充的内容可以用<font color="blue">MDC.put</font>方法随意设置;
-
此刻聪明的您应该能猜到jaeger官方的方案是如何实现的了,没错,就是借助MDC将trace信息填充到日志模板中,这样每行日志都有了trace信息,咱们在jaeger web页面中感兴趣的任何一次trace,都能找到对应的日志了
关于Jaeger的官方方案
- Jaeger的官方方案如下图所示,SDK已经把<font color="blue">traceId</font>、<font color="blue">spanId</font>、<font color="blue">sampled</font>写入当前线程的诊断上下文map(diagnostic context map),只要日志模板中配置上述三个变量,就会在所有业务日志中输出它们具体的值:
- 看起来似乎非常简单,那就动手编码试试吧
编码实战
-
jaeger与MDC的关联只是个小功能,没必要大张旗鼓的新建项目,基于《Jaeger开发入门(java版)》的代码继续开发即可,也就是说修改两个子工程<font color="blue">jaeger-service-consumer</font>和<font color="blue">jaeger-service-provider</font>的源码,让它们的业务日志打印出Jaeger的trace信息
-
首先从<font color="blue">jaeger-service-provider</font>工程开始,增加一个标准的logback日志配置文件<font color="red">logback.xml</font>,如下所示,日志模板中已添加了<font color="blue">traceId</font>、<font color="blue">spanId</font>、<font color="blue">sampled</font>变量:
<?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" scanPeriod="60 seconds" debug="false"> <contextName>logback</contextName> <!--输出到控制台--> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> <encoder> <!--%logger{10}表示类名过长时会自动缩写--> <pattern>%d{HH:mm:ss} [%thread] %-5level %logger{10} [traceId=%X{traceId} spanId=%X{spanId} sampled=%X{sampled}] %msg%n</pattern> <charset>utf-8</charset> </encoder> </appender> <root level="info"> <appender-ref ref="console" /> </root> </configuration>
- 再去检查配置类,确认JaegerTracer实例化时用了MDCScopeManager参数,如下所示,咱们在上一章已经这么做了,可以维持不变:
package com.bolingcavalry.jaeger.provider.config; import io.jaegertracing.internal.MDCScopeManager; import io.opentracing.contrib.java.spring.jaeger.starter.TracerBuilderCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class JaegerConfig { @Bean public TracerBuilderCustomizer mdcBuilderCustomizer() { // 1.8新特性,函数式接口 return builder -> builder.withScopeManager(new MDCScopeManager.Builder().build()); } }
- 接下来是在业务代码中随意加几行打印日志的代码,如下图红框所示:
- 接下来继续修改<font color="blue">jaeger-service-consumer</font>子工程,具体步骤与刚才改造<font color="blue">jaeger-service-provider</font>时一模一样,就不多占用篇幅赘述了,记得在业务代码中随意加几行日志,如下图红框:
- 开发完成,开始验证吧
验证
-
像《Jaeger开发入门(java版)》那样操作,将<font color="blue">jaeger-service-consumer</font>和<font color="blue">jaeger-service-provider</font>编译构建制作成docker镜像
-
用docker-compose将所有服务启动,然后通过浏览器访问<font color="blue">jaeger-service-consumer</font>的服务,多访问几次
-
打开jaeger的web页面,可以看到多次请求的trace,咱们随机选择一个,鼠标点击下图红框中的圆点:
- 此时会跳转到该trace的详情页,注意页面的url,如下图红框,里面的<font color="red">2037fe105d73f4a5</font>就是traceid:
- 用<font color="red">2037fe105d73f4a5</font>搜索<font color="blue">jaeger-service-provider</font>的日志,由于应用部署在docker中,咱们要用docker log和grep命令组合来过滤,如下所示,咱们代码写的日志都打印出来了,并且红框中就是traceid等关键信息
- 再去查看<font color="blue">jaeger-service-consumer</font>的日志,如下图红框,本次请求相关的日志也可以通过traceid搜索到:
- 至此,本篇实战就完成了,Jaeger的web页面上的任何一个trace,现在都能轻易找到与之对应的所有业务日志,这在定位问题时简直是如虎添翼的效果,如果您的系统用了ELK或者EFK来汇总所有分布式服务的日志,那就更高效了
你不孤单,欣宸原创一路相伴
欢迎关注公众号:程序员欣宸
微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界... https://github.com/zq2599/blog_demos

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
字典树之旅04.Patricia Trie(二)
字典树之旅系列文章: 开篇 Trie 的标准实现 Patricia Trie(一) Patricia Trie(二)【本文】 小结【待续】 1. 概述 上一篇文章中,我们用字符比较的方式编写代码实现了 Patricia Trie,但原论文采用的是二进制位比较的方式。那么,心里可能会有一些疑问: 原论文涉及到好多其它概念,两者为什么都是 Patricia Trie? 二进制位比较是如何推广到字符比较方式的? 二进制位比较与字符比较,两者分别有哪些优点和缺点? 那么,就带着这些疑问,继续我们的字典树之旅。相信看完这篇文章,您会找到自己的答案。 2. 由基数说起 2.1. 基数(radix) 基数(radix),在定位数系(positional numeral system)中表示不重复编码(digit)的数量。 譬如在10进制中,有 {0, 1, 2, ... ,8, 9} 共10个编码,那么基数就是10; 譬如在16进制中,有 {0, 1, 2, ... ,e, f} 共16个编码,那么基数就是16; ………… 然后,我们还可以采用 EASCII 字符编码来作为不重复编码的集合,那么其基...
- 下一篇
解放生产力「GitHub 热点速览 v.21.51」
作者:HelloGitHub-小鱼干 解放生产力一直都是我们共同追求的目标,能在摸鱼的空闲把💰赚了。而大部分好用的工具便能很好地解放我们的生产力,比如本周特推 RedisJSON 不用对 JSON 做哈希也能直接用 Redis 存储、读取 JSON 数据,省时省力。同样的,能节省生产力的 Turborepo 能免去多框架构建的繁琐,节省你的构建时间,还有 mold 链接器,性能蹭蹭地节省的都是你的时间。 以下内容摘录自微博@HelloGitHub 的 GitHub Trending 及 Hacker News 热帖(简称 HN 热帖),选项标准:新发布 | 实用 | 有趣,根据项目 release 时间分类,发布时间不超过 14 day 的项目会标注 New,无该标志则说明项目 release 超过半月。由于本文篇幅有限,还有部分项目未能在本文展示,望周知 🌝 本文目录 本周特推 1.1 Redis 原生 JSON:RedisJSON 1.2 高性能构建系统:turborepo GitHub Trending 周榜 2.1 射击游戏:space-shooter.c 2.2 任务管理...
相关文章
文章评论
共有0条评论来说两句吧...