Spring Boot 项目里日志别只会打 info:排错更高效的分层写法

Spring Boot 项目里日志别只会打 info:排错更高效的分层写法

不少 Spring Boot 项目刚开始写日志时都差不多:方法进来打一条、方法出去打一条、出异常再打一条。开发时看着热闹,真到线上查问题时,日志不是太多,就是没有重点。

日志这件事最容易走向两个极端:一种是全打,另一种是几乎不打。前者把排查现场淹没,后者到了出问题的时候完全没抓手。

更有效的做法不是把 info 打得更勤,而是先把日志分层。

第一层:业务关键动作日志

这类日志记录的是“业务真的发生了什么”,例如:

  1. 用户下单
  2. 状态流转
  3. 第三方回调接收
  4. 核心任务开始和结束

这层日志的重点不是技术细节,而是事件本身。它要帮助你回答:某件业务到底有没有发生、发生在什么时候、由谁触发、结果怎样。

如果这层日志都没有,很多问题连基本时间线都拼不出来。

第二层:链路排查日志

这层更偏工程化,主要解决“请求是怎么走到这里来的”。

例如:

  1. traceId
  2. 接口路径
  3. 调用耗时
  4. 下游服务返回码
  5. 数据库或缓存命中情况

这一层最适合跟请求链路、RPC、消息消费、异步任务绑在一起。它不一定需要很多文字,但字段要稳定,方便检索。

第三层:异常定位日志

异常日志最常见的错误不是没打,而是打得没有上下文。只打一段堆栈,查起来通常还是很费劲。

更有用的异常日志通常会补这些信息:

  1. 当前业务对象标识
  2. 关键参数摘要
  3. 当前阶段
  4. 下游依赖名称
  5. traceId 或请求标识

这样你看到异常时,不是只知道“报错了”,而是知道“在哪个阶段、对哪个对象、因为哪个依赖报错了”。

为什么很多项目日志很多但仍然难查

常见原因通常有三个:

  1. 没有统一格式
  2. 业务日志和调试日志混在一起
  3. 关键字段没有结构化

例如你所有日志都写成一句自然语言,看起来很直观,但一旦线上出问题,要按订单号、用户 ID、traceId 去聚合时,就会非常痛苦。

所以日志不只是“写给人看”,也要“写给检索系统看”。

结构化日志的收益非常直接

在 Spring Boot 项目里,如果能尽量把关键字段结构化输出,后面排错效率会明显高很多。

例如这些字段很值得固定下来:

  1. traceId
  2. userId
  3. orderId
  4. module
  5. action
  6. costMs
  7. result

哪怕你现在还没有完整的日志平台,后面只要开始接 ELK、OpenSearch 或云日志服务,这种结构就会立刻有价值。

info、warn、error 最好各管各的

很多项目最常见的问题是所有日志级别都乱用,最后 errorinfo 没什么区别。

我更倾向这样理解:

  1. `info` 记录关键正常事件
  2. `warn` 记录异常但可恢复、需关注的情况
  3. `error` 记录真正失败且需要介入的问题

如果把预期内的小异常也全打成 error,时间一长,大家就会对错误日志失去敏感度。

不要把敏感数据直接打出来

日志一旦进入集中平台,就意味着会有更多人能看到。所以下面这些内容要特别小心:

  1. 明文手机号
  2. 身份证号
  3. token
  4. 密码
  5. 完整支付信息

很多排错事故不是代码漏洞,而是日志泄漏。

更稳的做法是:

  1. 打摘要,不打全量
  2. 关键字段脱敏
  3. 敏感载荷默认不进普通日志

一个更适合 Spring Boot 中小项目的写法

如果项目规模还不大,我更建议先做到下面这些:

  1. 接口入口统一带 traceId
  2. 核心业务动作有明确 info 日志
  3. 下游调用记录耗时和结果
  4. 异常日志附带关键上下文
  5. 敏感信息默认脱敏

这五步不重,但已经能让日志从“有输出”变成“能排查”。

结语

日志真正的价值不在于数量,而在于能不能还原问题现场。对 Spring Boot 项目来说,比盲目多打一堆 info 更重要的,是把业务事件、链路信息和异常上下文分层。

当你开始按层设计日志后,线上排错会从“翻日志碰运气”,变成“沿着线索定位问题”。