欢送拜访我的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;@Configurationpublic 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来汇总所有分布式服务的日志,那就更高效了

你不孤独,欣宸原创一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 数据库+中间件系列
  6. DevOps系列

欢送关注公众号:程序员欣宸

微信搜寻「程序员欣宸」,我是欣宸,期待与您一起畅游Java世界...
https://github.com/zq2599/blog_demos