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

Log4j实现全局日志traceId详解

一、为何需要全局 traceId

在分布式系统中,一个请求可能会经过多个服务、多个线程。在日志中引入全局 traceId,可以让你轻松地追踪某一次请求全链路的日志,极大提升排障和分析效率。


二、实现方案总览

  1. 生成 traceId:每次请求生成唯一的 traceId(如 UUID)。
  2. 请求上下文传递 traceId:前端或上游服务发起请求时携带,或本服务自行生成,并在整个流程中传递。
  3. 日志输出 traceId:让 log4j 能在每条日志中输出 traceId,一般用MDC(Mapped Diagnostic Context)机制实现。

三、具体实现步骤(以 Java log4j2 为例)

1. 添加 log4j2 依赖

<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.x.x</version> </dependency>

2. 日志格式配置 TraceId(log4j2.xml)

在 log4j2.xml (或 log4j2.properties)中添加%X{traceId}

<PatternLayout pattern="[%d{ISO8601}] [%t] [%p] [traceId=%X{traceId}] %c{1} - %m%n"/>

3. 使用 MDC 设置和清理 TraceId

MDC是用来为被同一线程处理的日志增加上下文信息,这样这些信息可以自动加在日志里面。

生成和绑定 TraceId

【推荐:在每次请求开始时设置,请求结束后清除】

示例:

import org.apache.logging.log4j.ThreadContext; public class TraceIdUtil { public static void setTraceId() { String traceId = UUID.randomUUID().toString().replace("-", ""); ThreadContext.put("traceId", traceId); } public static void clear() { ThreadContext.remove("traceId"); } }
在 Controller 或 Filter 中设置和清理 TraceId

如 Spring Boot 项目推荐用 Filter 拦截每次 Web 请求:

@WebFilter(urlPatterns = "/*") public class TraceIdFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { String traceId = request.getHeader("X-TraceId"); if (traceId == null) { traceId = UUID.randomUUID().toString().replace("-", ""); } ThreadContext.put("traceId", traceId); chain.doFilter(request, response); } finally { ThreadContext.remove("traceId"); } } }

4. 日志打印

每次调用log.info()log.error()等,日志格式自动输出 traceId:

[2024-06-12 12:00:00.123] [main] [INFO] [traceId=acb1e2008ff94d919c0c2b761a17c234] MyService - foo started

四、分布式传递 TraceId

  • 服务间传递:前端或服务A调用服务B时,要将 traceId 作为请求头(如X-TraceId)传递,服务B读取后继续传递、写日志。
  • 多线程/异步:ThreadContext 只对当前线程有效,多线程需要手动传递 MDC 上下文,推荐工具如 MDC transmittable thread。

五、通用方案小结

  • 请求入口处生成/获取 traceId,并绑定到 MDC。
  • 日志模板加%X{traceId}占位符。
  • 记得请求结束后清除 traceId,防止线程复用产生污染。

六、补充:老版 log4j(log4j 1.x)

使用org.apache.log4j.MDC,方法类似。

七、TraceId 更好的生成和传递

  1. 生成策略

    • 可以用UUID或雪花算法。
    • 如果有调用链,可以拼接上下游 ID,或用 OpenTracing/Zipkin/SkyWalking 等分布式链路追踪工具。
  2. 传递策略

    • HTTP协议推荐用请求头,例如X-TraceId
    • MQ等异步通讯方式,将 traceId 放入消息体或消息header。
    • RPC自定义协议中,也要传递 traceId。

八、多线程与异步任务下 MDC 传递问题

1. 问题分析

log4j的MDC(ThreadContext)是线程本地变量,只对当前线程生效。如果你在主线程设置后,创建了新线程(比如用线程池、异步处理等),那么子线程默认无法感知traceId。

2. 解决方案

a. 手动传递MDC内容

在开启子线程、异步任务前,先获取主线程的MDC内容,然后在子线程恢复:

Map<String, String> mdcContext = ThreadContext.getImmutableContext(); // 获取主线程的MDC new Thread(() -> { ThreadContext.putAll(mdcContext); // 传递到子线程 // log输出时就有traceId了 }).start();

异步框架(如CompletableFuture)同理。

b. 使用中间件或MDC工具

推荐使用阿里开源 TransmittableThreadLocal,它能够自动让ThreadLocal(包括MDC)在线程池/异步任务传递。

示例,配合Spring:

// 引入依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>transmittable-thread-local</artifactId> <version>2.x.x</version> </dependency>

使用TTL提供的线程池包装,例如:

import com.alibaba.ttl.threadpool.TtlExecutors; ExecutorService ttlExecutorService = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(10)); // 用ttlExecutorService提交异步任务就能自动传递MDC上下文

这样异步日志也能打出正确的traceId。

c. Spring Sleuth/分布式链路追踪工具自动解决

Spring Cloud Sleuth、zipkin、skywalking可以自动在分布式调用和线程异步间传递traceId,无需自己维护MDC。


九、TraceId在微服务间的跨服务传递

  1. 前端到网关:建议前端/移动端生成唯一traceId(也叫requestId),作为请求头带着,Nginx等网关可以自动补齐。
  2. 网关到后端服务:网关会读取和透传traceId,服务收到后可写入MDC,继续向下游服务传递。
  3. 跨服务调用:在Feign/RestTemplate/HTTP Client等发起请求时,把traceId作为header带上。服务间只需要在入口Filter把traceId写入MDC,日志和下游都自动带上。

示例:

// 发起下游调用时,透传TraceId HttpHeaders headers = new HttpHeaders(); headers.add("X-TraceId", ThreadContext.get("traceId"));

十、日志查找的技巧和工具

  • 保证全链路所有服务日志都包含traceId,可用grep traceId=xxx一步查到完整链路。
  • 推荐配合ELK/Splunk/Skywalking/Zipkin等工具,可自动聚合traceId相同的日志,提供链路可视化查询。

十一、完整代码示例整理

对于完整Spring Boot集成,其实很简单:

  1. 创建一个拦截器/filter,一律set/clear TraceId到MDC。
  2. 日志格式配置好,比如:[traceId=%X{traceId}]
  3. 发起子线程或下游HTTP请求时把traceId带过去。

Filter 示例:

@Component public class TraceIdFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { String traceId = ((HttpServletRequest)request).getHeader("X-TraceId"); if (traceId == null) { traceId = UUID.randomUUID().toString().replace("-", ""); } ThreadContext.put("traceId", traceId); chain.doFilter(request, response); } finally { ThreadContext.remove("traceId"); } } }

十二、常见问题

  1. traceId丢失:线程池异步没传递、服务没透传都可能丢失,要关注这些点。
  2. traceId污染:未及时清理,导致协程/线程池任务复用,traceId混乱。一定要在请求结束/线程结束后ThreadContext.remove("traceId")
  3. 性能问题:MDC本身很轻量,但频繁操作还是建议简洁高效,特别是高并发场景。

总结

log4j下全局traceId最佳实践:

  • 请求入口统一生成/获取/存放到MDC
  • 日志格式加%X{traceId}
  • 子线程/异步场景主动传递MDC或用TTL
  • 服务间调用按约定自动透传traceId
  • 配合检索工具,链路聚合,定位效率极高
http://www.jsqmd.com/news/244336/

相关文章:

  • 多线程与并发-知识总结1
  • Java实习模拟面试|字节跳动业务中台后端校招一面面经:Kafka vs RabbitMQ、死锁避免、TCP握手与链表翻转深度解析
  • Java实习模拟面试|上海禾赛科技后端实习一面面经:高并发数据去重、事务与MQ一致性、反射争议与缓存选型深度解析
  • 深度学习毕设选题推荐:基于python-CNN训练识别蔬菜是否新鲜基于机器学习python-CNN训练识别蔬菜是否新鲜
  • 9个降aigc工具推荐,本科生高效避坑指南
  • Java实习模拟面试|字节跳动TTS后端校招二面面经:WaitGroup性能优化、分布式锁实现、线程安全LRU与Optional实战深度解析
  • 从参数竞赛到场景落地,收藏级干货助程序员和小白全面掌握AI大模型市场
  • 大模型黑箱揭秘:从分词到输出的全流程解析(程序员必看)
  • 收藏!无GPU也能做的大模型项目,3个零算力落地方案+完整学习路线,简历不再空白
  • 深度学习计算机毕设之基于python-CNN卷神经网络训练识别蔬菜是否新鲜基于python-CNN训练识别蔬菜是否新鲜
  • 收藏!面试必问:对称量化与非对称量化核心区别+实战选型指南
  • 深度学习毕设项目:基于python的识别蔬菜是否新鲜基于python-CNN训练识别蔬菜是否新鲜
  • Qwen2.5-VL大模型深度解析:从视觉感知到视频理解的全方位技术指南
  • 国外的文献资料在哪里查等相关问题解答
  • 大模型新人逆袭指南:从零到Offer的实战路径,项目经验+面试迭代双轮驱动
  • Node.js用spawn流式读取子进程输出
  • AI产品经理与普通产品经理的区别:不止懂算法,更要培养AI思维_大模型产品经理成长路线,AI大模型产品经理从零基础到进阶
  • 《创业之路》-853- 商业模式创新、技术创新的比较?
  • 计算机深度学习毕设实战-基于卷积神经网络识别花卉基于python_CNN卷积神经网络识别花卉
  • 深度学习毕设项目推荐-基于python_CNN卷积神经网络识别花卉
  • 写论文找不到外国文献?方法合集来了!
  • 运动控制告别单一 MCU,升级 电鱼智能 AM3354 提升多轴联动精度
  • 外文文献查找的6个途径等方法探讨
  • 国外期刊论文搜索网站推荐与使用指南
  • 东南亚拓客必备:2026最新Snapchat营销的必学7大策略
  • 拒绝搬运工!利用电鱼智能 RK3576 异构架构优化 ROS2 节点通信效率
  • 告别“网络延迟”:电鱼智能 RK3308 推动服务机器人语音模组向本地化转型
  • 深度学习计算机毕设之基于机器学习python_CNN卷积神经网络识别花卉基于python_CNN卷积神经网络识别花卉
  • 【课程设计/毕业设计】基于python_CNN卷积神经网络识别花卉基于python_CNN深度学习卷积神经网络识别花卉
  • 深度学习毕设项目:基于python的卷积神经网络识别花卉基于python_CNN卷积神经网络识别花卉