日志怎么记才有用:从 printf 到结构化日志

举报
yd_242458115 发表于 2026/10/29 08:36:19 2026/10/29
【摘要】 出故障时最痛苦的往往不是修不好,是"日志里什么都没写"。这篇讲清楚一条日志该带哪些字段、级别怎么分、什么时候该上结构化日志,以及三个常见的反模式。

线上出问题,登上去翻日志,看到满屏的:

start
processing...
done
error

哪个请求?哪台机器?什么时候开始的?错误码是多少? 一概没有。

这不是日志太少,是日志没有信息量。

一条有用的日志该带什么

最小可用集合是这五样:

字段 | 作用 | 反例

时间(带时区/毫秒) | 对齐多个服务的时序 | 10:23:01 不知道哪个时区

级别 | 快速过滤 | 所有东西都打成 INFO

来源 | 哪个服务/模块 | 只写 "error occurred"

上下文 ID | 串起一次请求的全链路 | 无

具体数据 | 到底发生了什么 | "操作失败" 不说为什么

其中上下文 ID(trace id / request id) 是最容易被忽略、也最值钱的一个。

有了它,一次用户请求经过五个服务,你可以把五段日志拼成一条时间线。没有它,你只能靠时间戳猜,而时间戳在并发场景下根本对不上。

日志级别怎么分才不乱

很多人把级别用成了三种:INFO、ERROR、还有"我也不知道该用哪个"。

一个可操作的划分:

级别 | 什么情况用 | 生产环境开不开

ERROR | 需要人介入的事(请求失败、数据不一致、外部依赖挂了) | 一直开

WARN | 异常但能自愈(重试成功、降级生效、接近阈值) | 一直开

INFO | 关键业务节点(请求进来、订单创建、任务完成) | 一直开,但要克制

DEBUG | 排查细节(参数、中间结果) | 默认关,按需开

判断标准:这条日志会让人半夜被叫起来吗? 会 → ERROR;不会但值得看一眼 → WARN;只是想知道流程走到哪 → INFO。

最常见的错误是把所有异常都打成 ERROR。结果 ERROR 一天几万条,没人看了,真正的故障被淹没。

能自愈的错误不该是 ERROR。 重试第二次成功了,那就是 WARN。

什么时候该上结构化日志

纯文本日志的问题:想按字段筛选就得写正则,而正则一改就可能漏。

结构化日志是把一条日志输出成一行 JSON:

{"ts":"2026-10-26T10:23:01.482+08:00","level":"ERROR","svc":"order","trace":"a3f9c2","msg":"payment timeout","orderId":"20261026001","elapsedMs":30012}

好处很直接:

  • 日志系统能直接按 orderId 查,不用正则
  • 能算指标(比如按 elapsedMs 统计 P99)
  • 不会因为日志文案改了就断掉监控规则

代价:人眼看不如纯文本舒服。

所以实践中的折中方案是:开发环境用可读格式,生产环境输出 JSON。大多数日志库都支持按环境切换格式。

如果只是个小脚本、日志量一天几十行,那就别上结构化,纯文本加好字段就够了。别为了架构感给自己加活。

三个常见反模式

反模式一:在循环里打日志

for (const item of items) { log.info("processing " + item.id) }

一万条数据就是一万行日志。磁盘写满、日志系统限流,真出问题时反而查不到。

做法:循环里只统计,循环外打一条汇总。

反模式二:日志里带敏感信息

这是最危险的一类。密码、token、完整手机号、身份证、银行卡,只要进了日志,就等于进了所有能看日志的人手里。

做法:输出前统一脱敏,只留前后各 4 位。而且要在打日志的那一层做,不要指望日志系统来过滤——它不知道你的业务字段是什么意思。

反模式三:异常吞掉只留一句 "failed"

try { doSomething() } catch (e) { log.error("failed") }

堆栈呢?错误码呢?

正确做法是把异常对象本身打出来,或者至少打 e.message 和 e.stack。丢掉堆栈的那一行日志,等于把排查线索扔了。

日志要不要轮转

要。而且这是最常见的"磁盘被日志撑满"的元凶。

一条最小的轮转配置:

/path/to/*.log {
    daily
    rotate 14
    compress
    missingok
    notifempty
    copytruncate
}

copytruncate 适用于不支持重新打开日志文件的程序(它先复制再清空,写入方不受影响)。

如果程序支持信号重开(很多守护进程支持 SIGUSR1),用 create 模式更干净。

注意权限:如果日志目录所属用户不是 root,logrotate 会以"目录权限不安全"为由跳过。这时需要在配置里加一行:

su 用户名 用户组

这个报错信息很不显眼,容易以为轮转生效了其实没有。

小结

三条能立刻用上的:

1. 每条日志带 trace id —— 这是排查效率提升最大的一步

2. 能自愈的错误别打 ERROR —— 保住 ERROR 的信噪比

3. 日志里绝不出现敏感明文 —— 在产生日志的那一层脱敏

日志不是写给自己看的,是写给三个月后半夜被叫起来的自己看的。按这个标准写,就不会写出满屏的 "start / done"。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。