在微服务架构中,一个用户请求往往需要经过多个服务协同处理。当系统出现问题时,如何快速定位问题?如何追踪请求的完整调用链路?
log-tracing 是一个轻量级、低侵入的分布式链路追踪组件,通过统一的 traceId 串联起分散在各个服务中的日志,让您能够:
- 在海量日志中快速检索出一个请求的所有相关日志
- 清晰地看到请求在各个服务间的流转路径和调用关系
- 快速定位问题发生在哪个服务、哪个环节
- 开箱即用:引入依赖即可使用,Spring Boot 自动配置
- 自动传递:HTTP、Feign、RabbitMQ 自动传递 traceId
- 异步支持:提供工具类和自动增强功能,解决线程池、
@Async、消息队列等异步场景的链路断裂问题 - SkyWalking 集成:与 SkyWalking 无缝集成,保持链路连续性
- 安全可控:支持 traceId 长度限制、格式验证,防止恶意攻击
- 灵活配置:支持自定义键名,避免与其他追踪组件冲突
- 按需启用:支持按需启用/禁用功能模块
log-tracing
├── log-tracing-core # 核心逻辑模块
│ ├── consts # 常量与枚举定义
│ ├── enhancer # 外部组件增强逻辑
│ │ └── rabbit # RabbitMQ 链路传递增强(支持 MDC 与 SkyWalking)
│ ├── generator # TraceId 生成策略接口及默认实现
│ ├── interceptor # 拦截器(Feign 客户端、WebService)
│ ├── toolkit # 链路追踪工具集
│ │ ├── TracedThreadPoolExecutor # 支持链路传递的 Java 原生线程池
│ │ ├── TracedThreadPoolTaskExecutor # 支持链路传递的 Spring 线程池
│ │ └── XxxTraceWrapper # 函数式接口装饰器(Runnable, Callable, etc.)
│ └── util # 工具类(MDC 操作、SkyWalking 状态检测等)
├── log-tracing-spring-boot-autoconfigure # 自动配置模块
│ ├── config # 各功能模块的条件装配配置
│ ├── processor # Bean 后置处理器(用于自动增强线程池、@Async 等)
│ └── TracingProperties # 属性配置类
└── log-tracing-spring-boot-starter # 启动器模块(快速引入依赖)
- Java 8+
- Spring Boot 2.7.x
- Spring Framework 5.3.x
- SkyWalking 9.3.0(可选)
- RabbitMQ(可选)
- Feign(可选)
本组件通过 JitPack 发布。
第一步:在项目根目录的 pom.xml 中添加 JitPack 仓库源:
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>第二步:添加组件依赖:
<dependency>
<groupId>com.github.mr-box.log-tracing</groupId>
<artifactId>log-tracing-spring-boot-starter</artifactId>
<version>1.0.1</version>
</dependency>组件会自动配置并启用以下功能:
- Web 请求链路追踪
- Feign 客户端链路追踪(如果使用了 Feign)
- Spring Rabbit 消息链路追踪(如果使用了 Spring Rabbit)
- SkyWalking 集成(如果使用了 SkyWalking Agent)
在 logback-spring.xml 中配置日志格式,添加 traceId:
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>启动应用后,查看日志,您会看到每条日志都包含了 traceId:
2024-01-23 10:30:15.123 [c466ca11-71c9-4dff-9fbe-d0471d421524] INFO c.e.UserService - 处理用户请求
2024-01-23 10:30:15.234 [c466ca11-71c9-4dff-9fbe-d0471d421524] INFO c.e.OrderService - 创建订单
| 配置项 | 默认值 | 说明 |
|---|---|---|
enabled |
true |
组件总开关 |
enable-pool-task-executor-enhance |
false |
是否自动增强所有 ThreadPoolTaskExecutor |
enable-async-configurer-executor-enhance |
false |
是否自动增强通过 AsyncConfigurer 配置的 @Async 线程池 |
web-interceptor.accept-header-trace-id |
true |
是否接受请求头中的 traceId |
web-interceptor.max-trace-id-length |
256 |
traceId 最大长度 |
web-interceptor.validate-trace-id-format |
false |
是否验证 traceId 格式 |
rabbitmq.enabled |
true |
是否启用 RabbitMQ 增强 |
key-names.mdc-trace-id |
traceId |
MDC 中的键名 |
key-names.http-header-trace-id |
X-TraceId |
HTTP Header 中的键名 |
key-names.mq-header-trace-id |
mdc-trace-id |
MQ Header 中的键名 |
在 application.yml 中进行配置,以下示例配置无特殊说明均为默认配置:
mr-box:
tracing:
# ========== 基础配置 ==========
# 组件总开关,默认开启
enabled: true
# 是否自动增强所有 ThreadPoolTaskExecutor Bean(默认:false)
# 开启后,Spring 容器中所有的 ThreadPoolTaskExecutor 都会自动支持链路追踪
enable-pool-task-executor-enhance: false
# 是否自动增强通过 AsyncConfigurer 配置的 @Async 线程池(默认:false)
# 开启后,通过实现 AsyncConfigurer 接口配置的 @Async 方法会自动传递 traceId
# 注意:仅对通过 AsyncConfigurer 方式配置的线程池生效,不影响其他配置方式
enable-async-configurer-executor-enhance: false
# ========== 追踪ID键名配置 ==========
# 用于避免与其他追踪组件(Sleuth、SkyWalking、OpenTelemetry)冲突
key-names:
# MDC中的trace id键名(默认:traceId)
mdc-trace-id: traceId
# HTTP请求头、响应头中的trace id键名(默认:X-TraceId)
http-header-trace-id: X-TraceId
# MQ消息header中的trace id键名(默认:mdc-trace-id)
mq-header-trace-id: mdc-trace-id
# ========== Web拦截器配置 ==========
web-interceptor:
# 是否接受请求头中的 traceId(默认:true)
# true: 从请求头中读取 traceId(适用于内部微服务)
# false: 忽略请求头中的 traceId,总是生成新的(适用于 最上游服务/API Gateway)
accept-header-trace-id: true
# traceId 最大长度限制(默认:256)
# 超过此长度将被截断并记录警告日志
# 设置为 0 或负数表示不限制长度
max-trace-id-length: 256
# 是否启用 traceId 格式验证(默认:false)
# true: 只接受符合格式规范的 traceId(字母、数字、中划线、下划线、点号)
# false: 不验证格式
# 建议:面向客户端的服务且 accept-header-trace-id=true 时,推荐启用
validate-trace-id-format: false
# ========== RabbitMQ配置 ==========
rabbitmq:
# 是否启用 RabbitMQ 的链路追踪增强(默认:true)
enabled: true
# 禁用的增强器名称列表(默认:空)
# 可选值:MDC, SkyWalking(不区分大小写)
disable-processors: []针对不同的服务类型,推荐使用不同的安全配置:
mr-box:
tracing:
web-interceptor:
accept-header-trace-id: false # 不接受客户端 traceId,防止恶意注入
max-trace-id-length: 256
validate-trace-id-format: falsemr-box:
tracing:
web-interceptor:
accept-header-trace-id: true # 接受上游服务 traceId
max-trace-id-length: 256 # 限制长度,防止超长攻击
validate-trace-id-format: true # 启用格式验证,防止日志注入mr-box:
tracing:
web-interceptor:
accept-header-trace-id: true
max-trace-id-length: 256
validate-trace-id-format: false为了避免与其他追踪组件冲突(如 Sleuth ),组件提供了自定义键名的功能,支持自定义键名:
mr-box:
tracing:
key-names:
http-header-trace-id: X-TraceId注意事项:
- 修改键名后,需要确保所有服务使用相同的配置
- 日志配置中的 MDC 键名需要同步修改
- 建议在项目初期就确定键名
组件会在以下场景自动传递 traceId:
| 场景 | 说明 | 配置要求 |
|---|---|---|
| HTTP 请求 | Web 请求自动传递 | 默认启用 |
| Feign 调用 | Feign 客户端自动传递 | 默认启用 |
| RabbitMQ 消息 | 消息生产和消费自动传递 | 默认启用 |
| SkyWalking | 与 SkyWalking 集成 | 需要 SkyWalking Agent |
增强所有 ThreadPoolTaskExecutor:
mr-box:
tracing:
enable-pool-task-executor-enhance: true开启后,Spring 容器中所有的 ThreadPoolTaskExecutor Bean 都会自动支持链路追踪,无需修改代码。
增强 @Async 线程池(通过 AsyncConfigurer 配置):
如果您通过实现 AsyncConfigurer 接口来配置 @Async 的线程池:
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.initialize();
return executor;
}
}可以开启自动增强:
mr-box:
tracing:
enable-async-configurer-executor-enhance: true开启后,@Async 注解的方法会自动传递 traceId,无需修改代码。
注意: 此配置仅对通过 AsyncConfigurer 接口配置的线程池生效。如果您使用其他方式配置 @Async(如 @Bean 方式),请使用方式一或方式二。
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
TracedThreadPoolTaskExecutor executor = new TracedThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("async-");
return executor;
}// Runnable 包装
Runnable task = RunnableTraceWrapper.of(() -> {
// 异步任务,可以获取主线程的 traceId
})
executor.execute(task);
// Callable 包装
Future<String> future = executor.submit(CallableTraceWrapper.of(() -> {
// 业务逻辑
}));
// Consumer 包装(适用于 Stream)
list.parallelStream().forEach(ConsumerTraceWrapper.of(item -> {
// 业务逻辑
}));
// Supplier 包装
Supplier<String> supplier = SupplierTraceWrapper.of(() -> {
// 业务逻辑
});
// Function 包装
Function<String, String> function = FunctionTraceWrapper.of(item -> {
// 业务逻辑
});// 获取当前 MDC 中的 traceId
Optional<String> traceId = TraceUtils.getMDCTraceId();
// 获取或生成 traceId
String traceId = TraceUtils.getOrGenerateMDCTraceId();// 设置 traceId
TraceUtils.setMDCTraceId("your-trace-id");
// 清理 traceId
TraceUtils.removeMDCTraceId();默认情况下,组件使用 SkyWalking的TID或UUID 作为 TraceId。如果需要自定义生成策略(如雪花算法、短ID、业务前缀等),可以实现 TraceIdGenerator 接口。
当存在多个生成器时,优先级顺序为:
手动设置 > Spring Bean > SPI > 默认实现
方式一:Spring Bean(推荐)
@Configuration
public class TracingConfig {
@Bean
public TraceIdGenerator traceIdGenerator() {
// 使用雪花算法生成数字ID
return new SnowflakeTraceIdGenerator(1L, 1L);
// 或使用短ID(16位随机字符串)
// return new ShortIdGenerator();
// 或使用自定义格式
// return () -> "TRC-" + System.currentTimeMillis() + "-" + UUID.randomUUID().toString().substring(0, 8);
}
}方式二:SPI 机制
- 实现
TraceIdGenerator接口 - 在
META-INF/services/com.github.mrbox.logtracing.core.generator.TraceIdGenerator文件中注册实现类
方式三:手动设置
适用于需要在运行时动态切换生成器的场景。
package com.example;
import com.github.mrbox.logtracing.core.generator.TraceIdGeneratorHolder;
import com.example.tracing.CustomTraceIdGenerator;
public class Application {
public static void main(String[] args) {
// 在应用启动时手动设置生成器
TraceIdGeneratorHolder.setGenerator(new CustomTraceIdGenerator());
// 启动 Spring Boot 应用
SpringApplication.run(Application.class, args);
}
}推荐方式:使用支持追踪的线程池
TracedThreadPoolTaskExecutor executor = new TracedThreadPoolTaskExecutor();
executor.initialize();
CompletableFuture
.runAsync(() -> { /* 任务1 */ }, executor)
.thenRunAsync(() -> { /* 任务2 */ }, executor)
.thenRun(() -> { /* 任务3 */ })
.exceptionally(ex -> { /* 异常处理 */ return null; });不推荐方式:手动包装所有任务
CompletableFuture
.runAsync(RunnableTraceWrapper.of(() -> { /* 任务1 */ }))
.thenRunAsync(RunnableTraceWrapper.of(() -> { /* 任务2 */ }))
.thenRun(RunnableTraceWrapper.of(() -> { /* 任务3 */ }))
.exceptionally(FunctionTraceWrapper.of(ex -> { return null; }));如果需要自定义 RabbitMQ 的线程池,注意不要使用 TracedThreadPoolTaskExecutor / TracedThreadPoolExecutor:
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
// ❌ 错误:使用增强的 TracedThreadPoolTaskExecutor
// ✅ 正确:使用普通的 ThreadPoolTaskExecutor
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
// ... 其他配置
executor.initialize();
factory.setTaskExecutor(executor);
return factory;
}原因: 如果使用了TracedXxxExecutor,每个消费者线程都会创建一个异步链路且一直复用。
虽然当开启SkyWalking增强时会在首次消费时终止该链路,仍建议尽量避免使用。
Logback 配置示例:
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/application.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>API Gateway:
mr-box:
tracing:
web-interceptor:
accept-header-trace-id: false # 不接受客户端 traceId业务服务:
mr-box:
tracing:
web-interceptor:
accept-header-trace-id: true # 接受上游 traceId
validate-trace-id-format: true # 启用格式验证
enable-pool-task-executor-enhance: true # 启用线程池自动增强消息消费者:
mr-box:
tracing:
rabbitmq:
enabled: true
enable-pool-task-executor-enhance: true检查清单:
-
确认组件已启用
mr-box: tracing: enabled: true
-
确认日志配置正确
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] ...</pattern>
-
确认 MDC 键名匹配
key-names: mdc-trace-id: traceId # 与日志配置中的 %X{traceId} 匹配
解决方案:
-
启用自动增强
enable-pool-task-executor-enhance: true
-
或使用
TracedThreadPoolTaskExecutor -
或手动包装任务
executor.execute(RunnableTraceWrapper.of(() -> { ... }));
检查清单:
-
确认 RabbitMQ 增强已启用
rabbitmq: enabled: true
-
检查是否禁用了增强器
rabbitmq: disable-processors: [] # 确保为空
A: 两者的侧重点完全不同。log-tracing 专注于解决“日志串联”问题,特别是针对 Spring RabbitMQ 消费者端 SkyWalking 链路断裂 的痛点进行了深度增强。
| 维度 | log-tracing | Spring Cloud Sleuth |
|---|---|---|
| 核心定位 | 轻量级日志增强工具 | 全功能链路追踪方案 |
| RabbitMQ 支持 | 深度增强 在串联业务日志的基础上,重点解决了 Consumer 端的 SkyWalking 链路断裂问题 |
基础支持 支持生产/消费者日志串联,但无法解决异步消息消费导致的 SkyWalking 链路断开 |
| SkyWalking 集成 | 深度协同 核心目标是保证 SkyWalking 链路在异步/消息场景下的连续性,同时也支持复用其 TraceId |
独立体系 通常作为独立的追踪体系运行,无法解决 SkyWalking 的链路连续性痛点 |
| 复杂度与性能 | 极低开销 仅操作 MDC 和 Header,适合对性能要求严苛且只需日志串联的场景 |
中等开销 包含 Span 管理、采样、耗时统计及数据上报,依赖较重 |
核心价值点: 如果您在使用 SkyWalking 时发现 RabbitMQ 消费者端链路断裂,或者觉得 Sleuth 太重、只想纯粹地串联日志,log-tracing 是更精准的选择。它能完美补齐 APM 工具(如 SkyWalking)在消息队列场景下的“断链”短板。
A: 当前版本基于 Spring Boot 2.7.x,Spring Boot 3.x 暂不支持。
A: 当前版本只支持 RabbitMQ,RocketMQ/Kafka 支持计划在后续版本中添加。
A: 只需在启动时添加 SkyWalking Agent,组件会自动集成:
java -javaagent:/path/to/skywalking-agent.jar -jar your-app.jar本项目采用 Apache License 2.0 许可证。
- Issues: GitHub Issues
- Email: wel.come@qq.com
⭐ 如果这个项目对您有帮助,请给个 Star!