SpringBoot Sleuth Zipkin Log日志链路追踪完整流程

SpringBoot Sleuth Zipkin Log日志链路追踪全流程

先拍砖,这个流程你跑不通,或者traceId找不到、Log4j2无法生成TraceId,直接来拍我,有一个我接一个!!!!!!

1.步骤

1)先下载Zipkin jar包

这样你可以在本地先启动Zipkin服务 我的版本是zipkin-server-2.12.9-exec.jar 下载地址 链接:https://pan.baidu.com/s/1BV61J0c4EwWzC8OnBwQY8Q
提取码:898k 可以下载zipkin jar包,

2)启动项目

SpringBoot项目,引入相关的jar包,和Sleuth和Zipkin相关的只需要这两个就够了,注意版本、版本、版本,重要事情说三遍,这里只是一部分Pom,完整Pom在下面步骤详解中

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>2020.0.1</spring-cloud.version>
	</properties>
	
<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-sleuth-zipkin</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

<!--注意 版本是有SpringCloud 版本控制的,在dependencyManagement 中控制版本-->		
<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
3)配置日志

本文先说LogBack日志配置,再说Log4j2日志配置,因为这俩中间存在坑,什么坑呢?LogBack 默认输出traceId信息,但是Log4j2 sleuth是不会输出TraceId的,而且即使你配置了 [%X{X-B3-TraceId},%X{X-B3-SpanId}] 依旧不会输出TraceId的,这点是深坑,具体原因请看一下问题排查
4)请求Controller,看日志是否打印TraceId、SpanId
5)看Zipkin UI界面,查看链路是否上传到Zipkin服务器,链路是否存在
6)大功告成

2.本地部署Zipkin服务器

下载jar包后,直接到jar包目录,运行 java -jar java -jar zipkin-server-2.12.9-exec.jar
当你看到 Started ZipkinServer in 2.652 seconds (JVM running for 3.204) 表明运行成功

1)运行Jar

在这里插入图片描述

2)启动Zipkin服务器

注意 http://127.0.0.1:9411 是Zipkin服务器界面的入口
看到 Serving HTTP at /0:0:0:0:0:0:0:0:9411 - http://127.0.0.1:9411/
Started ZipkinServer in 2.602 seconds (JVM running for 3.149) 说明启动成功
在这里插入图片描述

3)查看Zpikin界面

浏览器输入 http://127.0.0.1:9411 你可以看到一下界面
在这里插入图片描述

3.配置项目

本项目采用 SpringBoot 项目架构,然后引入了 Zipkin、Sleuth用来做全链路追踪

SpringBoot 2.4.1版本 + SpringCloud 2020.0.1版本 + Sleuth + Zipkin + Log4j2 日志

在https://start.spring.io/ 直接生成项目结构SpringBoot项目 trace ,jdk版本选择java 8 然后Gentrate 生成zip
构建项目、右侧依赖 注意选取 web、sleuth、zipkin模块作为依赖!!!
构建项目、右侧依赖 注意选取 web、sleuth、zipkin模块作为依赖!!!
构建项目、右侧依赖 注意选取 web、sleuth、zipkin模块作为依赖!!!
构建完成 ,解压zip包,然后 用IDEA导入项目

1)构建项目

构建完毕后,解压Zip 包, 然后在IDEA中导入项目
在这里插入图片描述

扫描二维码关注公众号,回复: 12476855 查看本文章
2)导入项目

打开IDEA 导入项目,然后 查看目录结构
在这里插入图片描述

LogBack版本运行

此时我们先运行LogBack版本的日志,看看是否能输出链路

1)先看一下LogBack配置的项目目录结构

看一下最终目录结构 Controller测试类, RestTemplate 用来封装http请求
在这里插入图片描述

2)配置application.properties

主要配置项目端口及Zipkin配置

#服务器端口
server.port=9527
spring.application.name=traceTest

#zipkin配置
spring.zipkin.base-url=http://127.0.0.1:9411/
spring.zipkin.enabled=true
spring.sleuth.sampler.probability=1
3)配置Pom文件

Pom文件要注意以下,因为引入了SpringCloud的依赖,Sleuth是依赖SpringCLoud版本的

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.jzj</groupId>
	<artifactId>trace</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>trace</name>
	<description>Demo project for Spring Boot Trace</description>
	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>2020.0.1</spring-cloud.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-sleuth-zipkin</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-sleuth</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

4)引入项目测试类Controller

在com.jzj.trace目录下新建 controller包,包中新建测试类SiteController, 保证测试http端口
http://localhost:9527/temp/ping
http://localhost:9527/temp/log
http://localhost:9527/temp/http

package com.jzj.trace.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.net.URI;


@ResponseBody
@RestController
@RequestMapping("temp")
public class SiteController {
    private static Logger log = LoggerFactory.getLogger(SiteController.class);


    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("ping")
    public Object ping() {
        log.info("进入ping");
        return "pong study";
    }

    @RequestMapping("log")
    public Object log() {
        log.info("this is info log");
        log.error("this is error log");
        log.debug("this is debug log");
        log.warn("this is warn log");
        log.trace("this is trace log");
        return "123";
    }

    @RequestMapping("http")
    public Object httpQuery() {

        String studyUrl = "http://localhost:9527/temp/ping";
        URI studyUri = URI.create(studyUrl);
        String study = restTemplate.getForObject(studyUri, String.class);
        log.info("study:{}", study);

        String floorUrl = "http://localhost:9527/temp/log";
        URI floorUri = URI.create(floorUrl);
        String floor = restTemplate.getForObject(floorUri, String.class);
        log.info("floor:{}", floor);

        String roomUrl = "http://localhost:9527/temp/ping";
        URI roomUri = URI.create(roomUrl);
        String room = restTemplate.getForObject(roomUri, String.class);
        log.info("room:{}", room);
        return study + "-------" + floor + "-------" + room + "-------";
    }


}

在com.jzj.trace包下新建rest,新建类RestTemplateConfig 用于使用RestTemplate封装http请求

package com.jzj.trace.rest;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

/**
 * RestTemplate配置类
 */
@Configuration
public class RestTemplateConfig {
 
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory){
        return new RestTemplate(factory);
    }
 
    @Bean
    public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(5000);//单位为ms
        factory.setConnectTimeout(5000);//单位为ms
        return factory;
    }
}
5)启动项目、测试Http请求

启动项目,服务端口为9527,浏览器访问 http://127.0.0.1:9527/temp/ping、http://127.0.0.1:9527/temp/log、http://127.0.0.1:9527/temp/http
可以看到正常请求
在这里插入图片描述

6)查看Log日志及Zipkin界面

Log日志 [,794dc49f7c54fb99,53fa82e35f2f7478] 分别表示
第一个:Spring配置的项目application名称
第二个:TraceId 就是链路ID
第三个:SpanId 每一次请求都是一个新的Span,不清楚Span的可以看看Zipkin的基本概念
在这里插入图片描述

7)Zipkin界面

Zipkin界面可以看到这几次的请求,这样LogBack 配置的到此为止
在这里插入图片描述
LogBack的全链路配置就到此为止,我们下面看一下Log4j2的日志配置




Log4j2版本运行

用了Log4j2 ,就可以忽略 上面LogBack的版本,直接按照这个配置,也是没问题的

1)配置application.properties

配置application.properties相比logback,log4j2需要指明日志配置的xml文件 如 resources下log4j下log4j2-dev.xml文件

#项目配置 区分logback配置,端口修改为9528
spring.application.name=traceTest
server.port=9528
#项目配置end


#zipkin配置
spring.zipkin.base-url=http://127.0.0.1:9411/
spring.zipkin.enabled=true
spring.sleuth.sampler.probability=1
#zipkin配置end

#Log4j2日志配置
logging.config=classpath:log4j/log4j2-dev.xml
#Log4j2日志配置end
2)修改Pom文件

Pom文件需要修改成引用Log4j2的日志jar包,springboot默认依赖logback,现在要修改pom文件,去掉logback包,添加log4j2 jar包作为日志输出依赖
主要操作为 :
1.去除web包下的logging依赖
2.添加log4j2jar包依赖
如果有spring-boot-starter ,也要排除 logging依赖

Pom如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.jzj</groupId>
	<artifactId>trace</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>trace</name>
	<description>Demo project for Spring Boot Trace</description>
	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>2020.0.1</spring-cloud.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<exclusions><!-- 去掉springboot log默认配置 -->
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- 引入log4j2依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j2</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-sleuth-zipkin</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-sleuth</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

3)引入Log4j2配置文件

在resources下新建log4j文件夹,然后新建 log4j2-dev.xml文件 修改自己的项目名
$PROJECT_NAME 及日志存储路径 $FILE_PATH
要想log4j2打印TraceId、SpanId,必要听别人说要配置 [%X{X-B3-TraceId},%X{X-B3-SpanId}] 没卵用,重要事情说三遍
这个版本的只有按照这个配置才是可以生效的[%X{traceId},%X{spanId}]
这个版本的只有按照这个配置才是可以生效的[%X{traceId},%X{spanId}]
这个版本的只有按照这个配置才是可以生效的[%X{traceId},%X{spanId}]
在这里插入图片描述
Log4j2-dev.xml 配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!--
Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,
当设置成trace时,可以看到log4j2内部各种详细输出
-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration monitorInterval="5">
    <!--日志级别以及优先级排序:
    OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL
    -->

    <!--变量配置-->
    <Properties>
        <!--
            格式化输出:
            %d表示日期,
            %thread表示线程名,
            %-5level:级别从左显示5个字符宽度
            %msg:日志消息,%n是换行符
        -->
        <!--
        %logger{36} 表示 Logger 名字最长36个字符
        -->
        <property name="LOG_PATTERN" value="[%d{yyy-MM-dd HH:mm:ss:SSS}]  [%X{traceId},%X{spanId}] %highlight{%-5level}[%thread] %style{%logger{36}}{cyan} : %msg%n" />

        <!-- 定义日志存储的路径,不要配置相对路径 -->
        <property name="PROJECT_NAME" value="trace" />
        <property name="FILE_PATH" value="E:\myworkspace\log\${PROJECT_NAME}" />
    </Properties>

    <appenders>
        <console name="Console" target="SYSTEM_OUT">
            <!--输出日志的格式-->
            <PatternLayout pattern="${LOG_PATTERN}" disableAnsi="false" noConsoleNoAnsi="false"/>

            <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>

        </console>

        <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用-->
        <File name="FileLog" fileName="${FILE_PATH}/test.log" append="false">
            <PatternLayout pattern="${LOG_PATTERN}"/>
        </File>

        <!--
        这个会打印出所有的info及以下级别的信息,每次大小超过size,
        则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,
        作为存档
        -->
        <RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log" filePattern="${FILE_PATH}/${PROJECT_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="20MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>

        <!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/warn.log" filePattern="${FILE_PATH}/${PROJECT_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="20MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>

        <!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
        <RollingFile name="RollingFileError" fileName="${FILE_PATH}/error.log" filePattern="${FILE_PATH}/${PROJECT_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <!--interval属性用来指定多久滚动一次,默认是1 hour-->
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="20MB"/>
            </Policies>
            <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
            <DefaultRolloverStrategy max="15"/>
        </RollingFile>

    </appenders>

    <!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
    <!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>

        <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
        <logger name="org.mybatis" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </logger>
        <!--监控系统信息-->
        <!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。-->
        <Logger name="org.springframework" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>

        <root level="info">
            <appender-ref ref="Console"/>
            <appender-ref ref="FileLog"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
        </root>
    </loggers>

</configuration>

4)引入项目测试类Controller

参考LogBack配置的 SiteController及RestTemplateConfig ,一样的配置 注意项目端口改成了9528,controller中的 resttemplate请求接口跟着改下
在com.jzj.trace目录下新建 controller包,包中新建测试类SiteController, 保证测试http端口
http://localhost:9528/temp/ping
http://localhost:9528/temp/log
http://localhost:9528/temp/http

package com.jzj.trace.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.net.URI;


@ResponseBody
@RestController
@RequestMapping("temp")
public class SiteController {
    private static Logger log = LoggerFactory.getLogger(SiteController.class);


    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("ping")
    public Object ping() {
        log.info("进入ping");
        return "pong study";
    }

    @RequestMapping("log")
    public Object log() {
        log.info("this is info log");
        log.error("this is error log");
        log.debug("this is debug log");
        log.warn("this is warn log");
        log.trace("this is trace log");
        return "123";
    }

    @RequestMapping("http")
    public Object httpQuery() {

        String studyUrl = "http://localhost:9528/temp/ping";
        URI studyUri = URI.create(studyUrl);
        String study = restTemplate.getForObject(studyUri, String.class);
        log.info("study:{}", study);

        String floorUrl = "http://localhost:9528/temp/log";
        URI floorUri = URI.create(floorUrl);
        String floor = restTemplate.getForObject(floorUri, String.class);
        log.info("floor:{}", floor);

        String roomUrl = "http://localhost:9528/temp/ping";
        URI roomUri = URI.create(roomUrl);
        String room = restTemplate.getForObject(roomUri, String.class);
        log.info("room:{}", room);
        return study + "-------" + floor + "-------" + room + "-------";
    }


}

在com.jzj.trace包下新建rest,新建类RestTemplateConfig 用于使用RestTemplate封装http请求

package com.jzj.trace.rest;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

/**
 * RestTemplate配置类
 */
@Configuration
public class RestTemplateConfig {
 
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory){
        return new RestTemplate(factory);
    }
 
    @Bean
    public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(5000);//单位为ms
        factory.setConnectTimeout(5000);//单位为ms
        return factory;
    }
}
5)启动项目、测试Http请求

启动项目,访问端口 9528 controller端口
http://localhost:9528/temp/log
http://localhost:9528/temp/http
在这里插入图片描述

6)查看Log日志及Zipkin界面

Console端Log日志
在这里插入图片描述

Zipkin界面
在这里插入图片描述

大功告成!
至此 我们完成了SpringBoot 、SpringCloud、Sleuth、Zipkin、Logback、Log4j2的配置,并和且实现了Zipkin的链路追踪过程
这个只是实现了 Http链路追踪,当然在微服务架构中,更重要的是RPC接口之间的链路

下一章我们开始 Dubbo RPC 接口的链路追踪