2. 日志级别规范:从 DEBUG 到 FATAL,到底该怎么用?
聊日志级别,很多团队吵过架。
有人把 WARN 当普通通知打,有人用 ERROR 记录临时异常,还有人干脆只用 INFO 和 ERROR 两个级别。结果呢?告警泛滥,排查问题像大海捞针。
我个人习惯是:级别标准必须在项目第一天定死,否则后面根本改不动。先看各级别的准确定义,再说怎么在实际中统一。
2.1 五个级别的核心定义
| 级别 | 含义 | 典型触发条件 | 是否触发告警 |
|---|---|---|---|
| DEBUG | 开发调试信息,面向开发者自己 | 变量值、调用链、SQL 参数 | 否 |
| INFO | 正常运行事件,系统状态变化 | 服务启动、请求开始/结束、关键流程步骤 | 否 |
| WARN | 潜在风险,需要关注但不影响当前功能 | 配置缺失使用默认值、重试首次失败、近限值警告 | 视阈值而定 |
| ERROR | 功能错误,当前操作失败但系统仍在运行 | 数据库连接失败、第三方接口返回 500、参数校验不通过 | 是 |
| FATAL | 致命错误,服务即将或已经不可用 | OOM、核心线程池耗尽、配置加载失败 | 立即告警+介入 |
2.2 每个级别怎么用?避坑指南
DEBUG:给自己看的,别出海
DEBUG 日志说白了是“开发期显微镜”。我在项目中遇到过生产环境 DEBUG 没关,结果一天打了几十 GB 数据,磁盘撑爆了。
使用场景:
- 进入关键方法时打印入参和重要中间值
- 循环中的计算过程
- 条件分支的流向判断
核心原则: 默认关闭,只在开发或排查时临时打开。千万别写 logger.debug("User {} logged in with role {}", userId, role) 这种高频日志——哪怕只是字符串拼接,在高并发下也扛不住。
INFO:记录业务痕迹,供复盘和审计用
INFO 是日志界的“正常人”。它告诉运维和监控系统:系统在正常工作。我习惯把 INFO 打在每一个有明确业务含义的节点上,比如订单创建、支付回调、定时任务执行完成。
但别滥用。我曾经看到有人把每次循环的 item 号都打 INFO,后来被监控系统负责人骂了一顿。记住:INFO 的输出量在正常业务量下应该可读,每秒几千条 INFO 就不叫 INFO 了。
WARN:黄牌警告,但不罚下
WARN 是最容易被忽略的级别,也是最容易被人随意使用的。它的本意是:系统当前没问题,但未来可能出问题。
举个例子:第三方接口超时,第一次重试失败但第二次成功。这不是 ERROR,但也不该是 INFO。我会打 WARN,并带上重试次数和超时时间。这样我们就能在监控上看到“接口抖动”的趋势。
ERROR:出错了,但别崩溃
ERROR 是大多数开发者最熟悉的级别。但要注意:一个 ERROR 应该代表一个完整的错误事件,而不是堆栈里每条信息都打。
我见过有人把 catch (Exception e) 里打印 logger.error("错误") 之后再加一行 logger.error(e.getMessage()),这完全是浪费。正确做法是:
try {
// 业务逻辑
} catch (SQLException e) {
logger.error("订单:{} 写入数据库失败, 事务已回滚", orderId, e);
// 只打一次,包含上下文和异常
}
这么做的原因:ERROR 日志要能被唯一检索。带上订单 ID、接口名、关键参数,你才能在告警里直接定位问题。
FATAL:最后的遗言
FATAL 很少见,但出现时必须认真对待。它的定义是:系统已经无法提供正常服务了。比如内存溢出后 JVM 即将退出,或者核心配置加载失败。这种情况下,打 FATAL 的同时通常伴随进程自杀或节点下线。
核心要点:
- FATAL 必须包含完整堆栈和上下文
- FATAL 应该直接触发最高优先级告警(短信/电话)
- 一个 FATAL 可能对应一个进程的结束,所以别在一个请求里打多个 FATAL
2.3 统一级别标准的三个关键动作
说了半天定义,实际落地才是重点。这里有三个实践建议:
- 制定团队级《日志级别对照卡》——列出每个模块常见场景对应的级别。比如“限流触发”是 WARN,“限流熔断”是 ERROR。贴在 Wiki 和 IDE 注释里,新人来了直接查表。
- 在代码规范中约束——用 Checkstyle / SpotBugs 或自定义规则拦截不合规的日志级别选择。比如禁止在 catch 块里打
logger.info,必须用logger.error。 - 定期审计——我每个季度会拉一次生产日志,按级别统计数量,揪出那些“不该是 ERROR 却打了 ERROR”或者“WARN 每秒几百条”的代码段,直接提 MR 修改。
2.4 一张图看清整个体系
下面这张图我经常在新手培训时用,它直观展示了日志级别从开发到告警的完整流向:
2.5 常见场景级别对照表 (速查)
| 场景 | 正确级别 | 常见错误 | 理由 |
|---|---|---|---|
| 请求参数校验失败 | WARN | ERROR | 是用户行为问题,非系统故障 |
| 数据库连接池耗尽 | FATAL | ERROR | 服务无法处理新请求 |
| 第三方 API 超时并降级 | ERROR | WARN 或 INFO | 功能已受影响,必须记录并告警 |
| 定时任务完成一次扫描 | INFO | DEBUG | 需要知道任务是否正常执行 |
| 循环中临时打印变量值 | DEBUG | INFO | 仅供开发调试用 |
好,日志级别规范这部分就这些。记住:级别用对了,告警才是有营养的,排查问题才能从“瞎猜”变成“有迹可循”。