Java版分布式微服务云开发架构 Spring Cloud+Spring Boot之Java应用日志如何与Jaeger的trace关联

本篇概览

  • 在正式开始前,咱们先来看一个具体的问题:

  1. 一次web请求可能有多条业务日志(log4j或者logback配置的那种),这和您写代码执行<font color="blue">log.info</font>的次数有关,假设有10条,那么十次请求就有一百条业务日志;
  2. 通过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的关联只是个小功能,没必要大张旗鼓的新建项目,也就是说修改两个子工程<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搜索到:

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值