当前位置: 首页 > news >正文

Springboot 集成 TraceID

要在 Spring Boot 2.5.15 中实现日志打印包含 traceID 的功能,最简便的方式是使用 Spring Cloud Sleuth。它能自动生成和传播 traceID,并将其嵌入到日志中,无需手动处理。以下是具体实现步骤:

一、核心原理

Spring Cloud Sleuth 会通过 MDC(Mapped Diagnostic Context)机制,将生成的 traceID 和 spanID 存储到线程上下文中。我们只需在日志格式中配置从 MDC 中获取这些信息,即可让每条日志都自动带上 traceID。

二、具体实现步骤

1. 添加依赖(pom.xml)

在 pom.xml 中引入 Sleuth 依赖(无需 Zipkin,仅需日志中的 traceID 时):

xml

<!-- 引入 Spring Cloud BOM 管理版本 -->
<dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>2020.0.6</version> <!-- 与 Spring Boot 2.5.15 匹配 --><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement><!-- Spring Cloud Sleuth:生成并传递 traceID -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency><!-- 确保有日志依赖(Spring Boot 默认已包含) -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId>
</dependency>

2. 配置日志格式(application.yml)

在配置文件中修改日志格式,添加从 MDC 中获取 traceId 和 spanId 的占位符:

yaml

spring:application:name: your-service-name # 服务名称(用于链路标识)sleuth:sampler:probability: 1.0 # 采样率 100%(开发环境建议,生产可按需调整)baggage:correlation-fields: traceId,spanId # 关联日志字段# 日志配置:关键是添加 [%X{traceId:-},%X{spanId:-}] 占位符
logging:pattern:# 控制台日志格式console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{traceId:-},%X{spanId:-}] %logger{50} - %msg%n"# 文件日志格式(如果需要输出到文件)file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{traceId:-},%X{spanId:-}] %logger{50} - %msg%n"# 日志文件路径(可选)file:name: logs/app.log
配置说明:
  • [%X{traceId:-},%X{spanId:-}]%X{key} 是从 MDC 中获取值的日志占位符,:- 表示如果获取不到则显示空(避免报错)。
  • spring.sleuth.sampler.probability:采样率设为 1.0 表示所有请求都生成 traceID,适合开发和测试环境。

3. 验证代码(无需额外编码)

Sleuth 会自动拦截所有 HTTP 请求、异步任务等,并注入 traceID。只需正常编写业务代码即可:

java

运行

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class DemoController {private static final Logger log = LoggerFactory.getLogger(DemoController.class);@GetMapping("/test")public String testTraceId() {log.info("这是一条包含 traceID 的日志");log.warn("这是一条警告日志,同样包含 traceID");return "查看控制台日志,检查是否包含 traceID";}
}

三、验证效果

  1. 启动 Spring Boot 应用;
  2. 访问接口 http://localhost:8080/test
  3. 查看控制台日志,会看到类似如下输出:

plaintext

2024-08-25 10:15:30.123 [http-nio-8080-exec-1] INFO  [abc1234567890,def7890123456] com.example.DemoController - 这是一条包含 traceID 的日志
2024-08-25 10:15:30.125 [http-nio-8080-exec-1] WARN  [abc1234567890,def7890123456] com.example.DemoController - 这是一条警告日志,同样包含 traceID

其中 abc1234567890 就是本次请求的全局唯一 traceID,所有相关日志都会携带相同的 traceID。

四、特殊场景处理

1. 异步方法日志包含 traceID

默认情况下,Sleuth 会处理 @Async 注解的异步方法,但需确保异步配置正确:

java

运行

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.initialize();return executor;}
}

使用异步方法时,日志会自动携带 traceID:

java

运行

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;@Service
public class DemoService {private static final Logger log = LoggerFactory.getLogger(DemoService.class);@Asyncpublic void asyncMethod() {log.info("异步方法中的日志,应包含 traceID");}
}

2. 手动获取 traceID

如果需要在代码中手动获取 traceID(如返回给前端),可注入 Tracer 工具类:

java

运行

import org.springframework.cloud.sleuth.Tracer;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class TraceIdController {private final Tracer tracer;// 构造注入 Tracerpublic TraceIdController(Tracer tracer) {this.tracer = tracer;}@GetMapping("/get-trace-id")public String getTraceId() {// 获取当前 traceIDString traceId = tracer.currentSpan().context().traceIdString();return "当前请求的 traceID 是:" + traceId;}
}

场景二:

原始项目有logback配置日志样式,当前logging 配置发现没有生效,没有生成 logs/app.log 并且 新打印日志并没有注入traceID和spenID 问题解决如下:

当项目中存在 logback.xml 配置时,Spring Boot 的 application.yml 中 logging 相关配置可能会被覆盖,导致 TraceID 无法注入日志。这是因为 Logback 配置文件的优先级高于 Spring Boot 全局配置。以下是具体解决方案:

一、核心原因分析

  1. 配置优先级冲突logback.xml 是 Logback 的原生配置文件,优先级高于 Spring Boot 的 application.yml 中的 logging 配置,导致 application.yml 中定义的日志格式(含 TraceID)和文件路径不生效。
  2. TraceID 注入依赖 Logback 配置:Sleuth 通过 MDC 注入的 traceId 和 spanId,需要在 Logback 的日志格式中显式配置才能输出。

二、解决方案:修改 logback.xml 配置

需直接在 logback.xml 中添加 TraceID/SpanID 占位符,并配置日志文件输出路径,确保与 Sleuth 集成。

1. 找到项目中的 logback.xml

通常位于 src/main/resources/logback.xml,日志中提示存在多个 logback.xml(如 newauditor-admin 和 newauditor-drools 模块),需确保修改的是当前启动模块使用的 logback.xml

2. 修改 logback.xml 配置

在日志格式中添加 [%X{traceId:-},%X{spanId:-}] 占位符,并配置文件输出路径。示例完整配置:

xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false"><!-- 定义日志输出格式(核心:添加 traceId 和 spanId) --><property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{traceId:-},%X{spanId:-}] %logger{50} - %msg%n" /><!-- 定义日志文件存储路径 --><property name="LOG_PATH" value="logs" /><property name="LOG_FILE_NAME" value="app" /><!-- 控制台输出 --><appender name="console" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!-- 使用含 TraceID 的格式 --><pattern>${LOG_PATTERN}</pattern></encoder></appender><!--  info 级别日志文件输出 --><appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>INFO</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 日志文件路径:logs/app.log --><fileNamePattern>${LOG_PATH}/${LOG_FILE_NAME}.%d{yyyy-MM-dd}.log</fileNamePattern><!-- 日志保留天数 --><maxHistory>30</maxHistory></rollingPolicy><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>${LOG_PATTERN}</pattern></encoder></appender><!-- warn 级别日志文件输出 --><appender name="file_warn" class="ch.qos.logback.core.rolling.RollingFileAppender"><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>WARN</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_PATH}/${LOG_FILE_NAME}-warn.%d{yyyy-MM-dd}.log</fileNamePattern><maxHistory>30</maxHistory></rollingPolicy><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>${LOG_PATTERN}</pattern></encoder></appender><!-- error 级别日志文件输出 --><appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${LOG_PATH}/${LOG_FILE_NAME}-error.%d{yyyy-MM-dd}.log</fileNamePattern><maxHistory>30</maxHistory></rollingPolicy><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><pattern>${LOG_PATTERN}</pattern></encoder></appender><!-- 日志输出级别 --><root level="INFO"><appender-ref ref="console" /><appender-ref ref="file_info" /><appender-ref ref="file_warn" /><appender-ref ref="file_error" /></root><!-- 自定义包日志级别(可选) --><logger name="com.newtouch.ai.newauditor" level="INFO" additivity="false"><appender-ref ref="console" /><appender-ref ref="file_info" /></logger>
</configuration>

3. 关键配置说明

  • 日志格式[%X{traceId:-},%X{spanId:-}] 是从 MDC 中获取 Sleuth 注入的 TraceID 和 SpanID,:- 表示如果获取不到则显示空(避免日志报错)。
  • 日志路径:通过 LOG_PATH 和 LOG_FILE_NAME 定义日志文件路径为 logs/app.log,与 application.yml 中期望的路径一致。
  • 输出级别:确保 root 或自定义包的日志级别至少为 INFO(避免日志被过滤)。

三、验证配置是否生效

  1. 删除冲突配置:若 application.yml 中仍有 logging 配置,建议注释或删除(避免与 logback.xml 冲突)。
  2. 重启项目:启动后观察:
    • 控制台日志是否包含 [traceId,spanId] 格式(如 [a1b2c3d4e5f67890,a1b2c3d4e5f67890]);
    • 项目根目录下是否生成 logs 文件夹及 app.log 文件;
    • 访问接口后,检查日志文件中是否有带 TraceID 的日志。

四、特殊场景处理

1. 多模块项目 logback.xml 冲突

日志中提示 logback.xml 存在多个(如 newauditor-admin 和 newauditor-drools):

  • 确保启动模块(如 newauditor-admin)的 logback.xml 是最终生效的配置;
  • 其他模块的 logback.xml 可删除或统一继承主模块配置(通过 <include> 标签引入)。

2. 异步线程日志不显示 TraceID

若项目中使用异步线程(如 @Async),需配置 Sleuth 对异步线程的支持:

java

运行

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;@Configuration
@EnableAsync
public class AsyncConfig {@Beanpublic Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);// 关键:设置线程名称前缀,便于日志追踪executor.setThreadNamePrefix("Async-");// 初始化线程池executor.initialize();return executor;}
}

场景三:显著追加traceID,和 spanID字段

<property name="log.pattern" value="%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level [traceID:%X{traceId:-},spanID:%X{spanId:-}] %logger{20} - [%method,%line] - %msg%n" />

总结

核心是通过修改 logback.xml 配置,在日志格式中添加 TraceID/SpanID 占位符,并正确配置日志文件路径。由于 logback.xml 优先级高于 application.yml,必须在此文件中完成所有日志相关配置,才能确保 Sleuth 生成的 TraceID 正常输出到日志中。

五、总结

通过以上配置,Spring Boot 应用的所有日志(包括控制器、服务、工具类等)都会自动包含 traceID,实现了:

  1. 单条请求的所有日志通过同一个 traceID 关联;
  2. 跨服务调用时 traceID 自动传递(如果有多个服务);
  3. 无需手动埋点,Sleuth 自动处理。

这种方式既能满足日志查询和问题定位的需求,又能最小化代码侵入性。bo t 

http://www.dtcms.com/a/350236.html

相关文章:

  • 在react里使用路由,手动跳转
  • C++ 内存安全与智能指针深度解析
  • 【flutter对屏幕底部有手势区域(如:一条横杠)导致出现重叠遮挡】
  • YOLOv7:重新定义实时目标检测的技术突破
  • 浅聊RLVR
  • 绿色循环经济下的旧物回收App:重构闲置资源的价值链条
  • 设计仿真 | 从物理扫描到虚拟检具:Simufact Welding革新汽车零部件检测
  • 汽车零部件工厂ESOP系统工业一体机如何选型
  • 基于51单片机红外避障车辆高速汽车测速仪表设计
  • AEB 强制来临,东软睿驰Next-Cube-Lite有望成为汽车安全普惠“破局器”
  • kubeadm join 命令无法加入node节点,ip_forward 内核参数没有被正确设置
  • IIS 安装了.netcore运行时 还是报错 HTTP 错误 500.19
  • k8s笔记03-常用操作命令
  • Qt开发:智能指针的介绍和使用
  • 君正T31学习(二)- USB烧录
  • 支持指令流水的计算机系统设计与实现
  • mysql绿色版本教程
  • 【python断言插件responses_validator使用】
  • 校园科研自动气象站:藏在校园里的 “科研小站”
  • Nginx零拷贝技术深度解析
  • 【 Python程序员的Ubuntu入门指南】
  • Python二进制、八进制与十六进制高级操作指南:从底层处理到工程实践
  • freqtrade进行回测
  • 关于熵减 - 电力磁力和万有引力
  • list容器的使用
  • 15、IWDG独立看门狗
  • MTK Android 14 通过属性控制系统设置显示双栏或者单栏
  • VUE 的弹出框实现图片预览和视频预览
  • (多线程)线程安全和线程不安全 产生的原因 synchronized关键字 synchronized可重入特性死锁 如何避免死锁 内存可见性
  • React Native核心技术深度解析_Trip Footprints