日志的艺术:日志级别与最佳实践

日志这东西,说白了就是程序的「黑匣子」。

我刚入行那会儿,觉得日志就是随便打印点东西,方便自己调试。直到有一次线上事故,日志全是一堆无意义的 DEBUG 信息,关键错误被淹没了。嗯,那次我被老板叫去喝茶了。

从那以后,我养成了一个习惯:把日志当成代码的一部分来设计。

日志级别:你的程序该说什么话

日志级别不是摆设。它决定了你的程序在什么情况下,该说什么话。

级别 含义 我什么时候用
DEBUG 调试信息 开发环境、定位问题时才打开。线上环境默认关闭。
INFO 关键流程 用户登录、订单创建、服务启动。这些信息要让人看得懂。
WARN 潜在风险 配置缺失但用了默认值、接口响应慢但没超时。别忽视它。
ERROR 明确故障 数据库连不上、空指针、业务校验失败。必须立刻处理。

我个人习惯,线上环境只开 INFO 及以上。DEBUG 日志太吵了,你想想看,每秒几万条日志,谁看得过来?

我曾经犯过的错: 把敏感信息(比如用户密码、身份证号)直接打到了 ERROR 日志里。结果日志文件被运维同事看到了……嗯,从那以后我加了个日志脱敏过滤器。

结构化日志:别让日志变成「天书」

传统的日志长这样:

2024-01-15 10:23:45 ERROR 用户下单失败:订单号12345,原因:库存不足

看着还行?但如果你要写脚本分析它,你就得写正则表达式去匹配「订单号」和「原因」。麻烦不说,还容易出错。

结构化日志就不一样了。它把信息拆成键值对:

{
  "timestamp": "2024-01-15T10:23:45.123Z",
  "level": "ERROR",
  "service": "order-service",
  "traceId": "abc-123-def",
  "userId": 10086,
  "orderId": "ORD20240115001",
  "errorCode": "STOCK_INSUFFICIENT",
  "message": "用户下单失败"
}

你看,这样多清晰。用 Elasticsearch 或者 Loki 一搜,直接就能定位到问题。

我推荐用 JSON 格式。大部分日志框架都支持,比如 Logback 的 JsonLayout,或者直接集成 logstash-logback-encoder。

小技巧: 每个请求都带上一个 traceId。这样你就能把一次请求的所有日志串起来。微服务架构下,这个尤其重要。

日志最佳实践:我踩过的坑,你别再踩了

这些年我总结了几条铁律,分享给你:

  • 别打无意义的日志。 比如 logger.debug("进入方法")。这种日志除了占磁盘空间,一点用没有。
  • 错误日志要带上上下文。 别只打「操作失败」。你得告诉我:哪个用户?哪个订单?什么原因?
  • 日志别打得太频繁。 循环里打日志?小心你的磁盘 IO 被打满。我见过一个同事在 for 循环里打 DEBUG 日志,结果线上服务直接卡死。
  • 日志文件要轮转。 按天、按大小切分。别让一个日志文件撑爆磁盘。
  • 敏感信息必须脱敏。 手机号、身份证、密码,一律用 *** 替代。

核心原则: 日志是给人看的,也是给机器分析的。所以它既要可读,又要可解析。

知识体系:一张图看懂日志

下面这张图,是我自己画的。它把日志的核心逻辑串起来了:

日志核心知识体系 日志级别 结构化日志 最佳实践 工具链 DEBUG INFO WARN ERROR (线上慎用 DEBUG) JSON 格式 键值对 traceId 可解析 (推荐) 带上上下文 避免循环打日志 日志轮转 敏感信息脱敏 (别踩坑) ELK Loki Grafana Logback (选你喜欢的) 目标:可读 + 可解析 + 可追溯

你看,日志这件事,说简单也简单,说复杂也复杂。但只要你把上面这些点都做到位了,线上出问题的时候,你就能比别人快 10 倍定位到根因。

嗯,这就是日志的艺术。

可可精品资料库