【后端】【Java】一文详解Spring Boot 统一日志与链路追踪实践

Spring Boot 统一日志与链路追踪实践

在真实的 Spring Boot 项目中,仅仅"能跑"远远不够。
能定位问题、能还原请求、能快速排障,才是一个成熟后端系统的核心能力。

而这一切,都离不开 统一日志与链路追踪(Trace)


一、为什么要做统一日志与链路追踪?

如果没有统一日志,常见问题包括:

  • 日志格式杂乱无章

  • 不同接口日志风格不一致

  • 一次请求的日志散落在多行、多个类中

  • 微服务场景下无法串联完整调用链

❌ 出现线上问题时,只能"猜"。


✅ 统一日志与链路追踪能解决什么?

  • 一次请求 = 一个 TraceId

  • 所有日志都能被串起来

  • 快速定位慢接口、异常接口

  • 为监控、告警、埋点提供基础数据


二、日志、链路追踪的整体设计思路

推荐分层设计

复制代码
请求入口(Filter)
   ↓
生成 TraceId
   ↓
放入 MDC(日志上下文)
   ↓
业务日志自动携带 TraceId
   ↓
请求结束清理 MDC

三、Spring Boot 日志体系基础

Spring Boot 默认使用:

  • SLF4J(门面)

  • Logback(实现)

推荐日志使用方式

复制代码
private static final Logger log = LoggerFactory.getLogger(UserController.class);

不要直接使用:

复制代码
System.out.println()

四、什么是链路追踪(Trace)?

核心概念

概念 含义
TraceId 一次请求的唯一标识
Span 调用链中的一个节点
MDC 日志上下文存储

在单体应用中:

TraceId 就足够


五、基于 MDC 的链路追踪实现

1️⃣ 什么是 MDC?

MDC(Mapped Diagnostic Context)是 Logback 提供的 线程级上下文变量存储

复制代码
MDC.put("traceId", "xxx");

日志中即可自动打印。


2️⃣ 请求入口生成 TraceId(Filter)

复制代码
@Component
public class TraceFilter implements Filter {

    private static final String TRACE_ID = "traceId";

    @Override
    public void doFilter(ServletRequest request,
                         ServletResponse response,
                         FilterChain chain) {

        try {
            String traceId = UUID.randomUUID().toString().replace("-", "");
            MDC.put(TRACE_ID, traceId);
            chain.doFilter(request, response);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            MDC.remove(TRACE_ID);
        }
    }
}

3️⃣ 日志配置中打印 TraceId(logback-spring.xml)

复制代码
<pattern>
    %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread]
    %-5level %logger{36}
    [traceId=%X{traceId}]
    - %msg%n
</pattern>

4️⃣ 日志效果示例

复制代码
2025-01-01 10:00:01.123 [http-nio-8080-exec-1]
INFO UserController [traceId=9f8a3b7c2d1a4e]
- 查询用户信息

👉 同一次请求的所有日志 traceId 完全一致


六、统一请求日志(接口维度)

使用 HandlerInterceptor

复制代码
@Component
public class LogInterceptor implements HandlerInterceptor {

    private static final Logger log = LoggerFactory.getLogger(LogInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) {

        log.info("请求开始: {} {}", request.getMethod(), request.getRequestURI());
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) {

        log.info("请求结束: {} {}", request.getMethod(), request.getRequestURI());
    }
}

注册拦截器

复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor())
                .addPathPatterns("/**");
    }
}

七、记录接口耗时(性能关键)

复制代码
long start = System.currentTimeMillis();
// 业务逻辑
long cost = System.currentTimeMillis() - start;
log.info("接口耗时: {} ms", cost);

推荐统一在拦截器中处理,避免重复代码。


八、异常日志统一记录

结合全局异常处理:

复制代码
@ExceptionHandler(Exception.class)
public ApiResponse<Void> handleException(Exception e) {
    log.error("系统异常", e);
    return ApiResponse.error(500, "服务器内部错误");
}

👉 异常日志 必须打印堆栈


九、日志分级规范(非常重要)

级别 使用场景
DEBUG 开发调试
INFO 关键业务流程
WARN 可预期异常
ERROR 系统异常

❌ 不要滥用 ERROR

❌ 不要把正常流程打成 WARN


十、微服务场景下的链路追踪(扩展)

1️⃣ TraceId 透传

  • 网关生成 TraceId

  • HTTP Header 传递:

    X-Trace-Id: xxx

2️⃣ Spring Cloud 场景

  • Sleuth(旧)

  • Micrometer Tracing(新)

  • Zipkin / SkyWalking


十一、统一日志最佳实践总结

✅ Filter 生成 TraceId

✅ MDC 保存上下文

✅ 日志格式统一

✅ 接口日志 + 异常日志

✅ 日志分级清晰


十二、总结

统一日志与链路追踪,是后端系统"可运维性"的基石。

在 Spring Boot 项目中:

  • MDC 是最低成本的链路追踪方案

  • 单体应用足够用

  • 微服务可平滑升级到专业链路系统

写日志不是为了"看",

而是为了 在出问题时能快速定位问题

相关推荐
mldong5 小时前
引擎从不发一条消息:jeeflow 的两类扩展点与消息模块的分工
java·架构
君顾15 小时前
智慧零售实战指南:从技术架构到落地方案全解析
java·开发语言·零售
南棱笑笑生5 小时前
20260904实测给飞凌OK3576-C开发板刷入Rockchip原厂的IMG固件【使用飞凌的DTS】切换串口波特率为1.5Mbps
c语言·开发语言·rockchip
zhanghe6876 小时前
es的密码带有特殊字符,导出
java
平头哥技术团队6 小时前
Day 10 | 工欲善其事:VS Code 配置与项目归档
android·开发语言·前端·javascript·html·交互
pnoker6 小时前
IoT DC3 概念解读:把设备抽象成一套语义模板——位号、指令、事件与面向智能体的设备建模
java·人工智能·物联网·iot·dc3
乌萨奇也要立志学C++6 小时前
【洛谷】kmp算法
开发语言·算法
my_realmy6 小时前
Java SE 基础学习笔记(一):面向对象、继承多态、抽象接口与异常(JDK 8)
java·多线程·并发··生产者消费者模式
传奇开心果编程6 小时前
【Rust入门知识点学与练】第4课:条件判断 if / else
开发语言·学习·rust
m0_380743876 小时前
给 Claude API 调用补上超时重试和错误分类
开发语言·人工智能·php