同一条错误日志吵醒我 400 次:日志告警从「见过一次」到「正在发生」的分层改造

概述 今年 8 月的一个凌晨,1 点 12 分,我的手机开始连续震动。 某电商平台的结账链路出问题了:支付网关的一个下游连接池被打满,结算服务开始疯狂重试,日志里刷满了 PAYMENT_GATEWAY_TIMEOUT。值班同学在 1 点 19 分就定位到了根因,把连接池上限和超时重试次数调回合理值,1 点 59 分流量恢复。 故障本身不复杂,47 分钟结束。真正让人崩溃的是我的手机:这 10 分钟里,告警通道收到了 400 多条通知,全部来自同一条规则,全部指向同一个问题。 规则长这样: - alert: CheckoutPaymentTimeout expr: count_over_time({app="checkout"} |= "PAYMENT_GATEWAY_TIMEOUT" [1m]) > 0 labels: severity: critical 字面上它没错——日志里确实出现了 PAYMENT_GATEWAY_TIMEOUT。但它表达的意思是"过去 1 分钟里出现过一次",而它被当成了"支付正在坏"。于是每过 1 分钟,日志被"看到"一次,就报一次警;再乘上匹配到的 pod 数量,47 分钟就是几百条。 复盘的时候,我写了一句话贴在值班群里:日志告警刷屏,九成不是阈值设错了,而是把"发生过"当成了"正在发生"。 这篇文章讲的就是怎么修这个问题。核心做法是一句话:日志告警要分两级——记录规则(recording rule)先把事件流转成可以判断的状态指标,告警规则只在状态持续时报警。 日志告警不该是"日志里出现关键词就响",它应该是一个状态机。 拆成 7 个部分: 一、日志告警的边界:它适合管黑盒组件、事件型告警和高基数来源,不适合管资源水位和可用性 二、误报的根因:日志是事件流不是状态,count_over_time > 0 天生会刷屏,以及为什么必须分两级 三、第一级:记录规则:把"出现过"变成"多快、多密、持续多久",标签收敛到什么程度算够 四、第二级:告警规则:for 怎么用足、阈值为什么不能用 > 0、limit 设错会静默丢告警、多租户路由限制 五、ruler 的四个生产陷阱:WAL 回放期间不评估、ring 变更没有责任交接、local 存储无 glob、本地评估与 Grafana 不一致 六、落地顺序:从规则改造到值班口径的 5 个检查点 七、常见反模式清单:七种我都在线上见过的写法,每一种都能让前面的功夫白做 如果你还在用"日志里出现 ERROR 就报警",这篇基本能帮你把夜间被打断的次数降一个数量级。日志采集层怎么搭是另一半问题,可以看相关文章:日志监控体系:Loki + Promtail 部署。...

October 30, 2026 · 10 分钟 · 2113 字 · 徐保金