可观测性 — 在用户投诉之前知道坏了
一句话:可观测性不是装个监控面板,而是让系统能回答三个问题——坏了吗?哪里坏?为什么坏?——并且答案来自数据,不是来自猜。
🎯 这 20% 能解决哪些问题
| 你遇到的困惑 | 本质原因 | 马上能改的动作 |
|---|
| 用户投诉了才知道挂了 | 只监控了服务器存活,没监控用户链路 | 给核心接口埋延迟、错误、流量三个指标 |
| 告警一天几十条,全都忽略 | 告警报的是原因(CPU 高),不是症状 | 只对「用户感知的失败」告警,原因类进看板 |
| 日志一堆,查问题靠 grep | 日志非结构化,没有请求标识 | 统一 JSON 日志 + trace_id 贯穿全链路 |
| 平均延迟 100ms,用户却说卡 | 平均值掩盖长尾 | 改看 P95/P99,并把分位写进看板 |
| 监控费用比服务器还贵 | 全量打点、全量日志、高基数标签 | 采样 + 分级保留 + 禁止用用户 ID 做标签 |
一、三支柱:各管一段,不能互相替代
| 支柱 | 回答什么问题 | 成本 | 典型工具 | 保留期 |
|---|
| 指标 Metrics | 整体是否异常、趋势如何 | 低(聚合数字) | Prometheus / VictoriaMetrics | 长(月~年) |
| 日志 Logs | 具体哪一次、报了什么错 | 高(文本量大) | Loki / ELK | 短(7–30 天) |
| 链路 Traces | 一次请求慢在哪一段 | 中(采样) | Jaeger / Tempo | 中(3–7 天) |
核心规律:指标负责「发现」,链路负责「定位」,日志负责「看清细节」。顺序不能颠倒——先用指标发现异常,再用链路与日志下钻。
flowchart LR
A["指标异常<br/>错误率上升"] --> B["链路下钻<br/>哪一段慢/错"]
B --> C["日志细看<br/>具体异常与参数"]
C --> D["修复与验证"]
二、四个黄金信号:任何服务都先埋这四个
| 信号 | 指标定义 | 看什么 | 阈值示例 |
|---|
| 延迟 Latency | 请求耗时直方图 | P95 / P99,区分成功与失败请求 | P99 < 300ms |
| 流量 Traffic | QPS / 每分钟请求数 | 突增突降(突降常是上游挂了) | 相对基线的 ±30% |
| 错误 Errors | 失败请求占比(含超时) | 5xx、业务错误码、超时 | < 0.5% |
| 饱和度 Saturation | CPU、内存、连接池、队列积压 | 最紧张的那一项资源 | < 80% |
2.1 两套速记法
| 方法 | 适用对象 | 内容 |
|---|
| RED | 请求型服务(API、Web) | Rate 请求率、Errors 错误率、Duration 耗时 |
| USE | 资源型对象(CPU、磁盘、连接池) | Utilization 使用率、Saturation 饱和度、Errors 错误数 |
三、指标:命名、分位与基数
| 规则 | 正确做法 | 错误做法 |
|---|
| 命名 | http_request_duration_seconds(单位进名字) | time、cost 无单位 |
| 类型 | 计数用 counter,瞬时值用 gauge,分布用 histogram | 用 gauge 存累计值 |
| 分位 | 在看板与告警里用 P95/P99 | 只报平均值 |
| 标签 | 只放低基数字段:服务、接口、状态码、区域 | 放 user_id、订单号、URL 全路径 |
| 聚合 | 先按接口分组,再看全局 | 全局平均掩盖单接口崩 |
基数陷阱:标签每多一个高基数字段,时间序列数量成倍增长。user_id 做标签,一周就能把监控打挂。
四、日志:结构化才有用
| 要素 | 要求 |
|---|
| 格式 | JSON,一行一条,禁止多行拼接 |
| 必带字段 | timestamp、level、service、trace_id、user_id(可脱敏)、message |
| 级别 | ERROR 只用于「需要人处理」,WARN 用于可自愈异常,INFO 记录关键业务节点 |
| 敏感信息 | 手机号/身份证/密码/token 一律脱敏或剔除 |
| 采样 | 高频 INFO 采样 1%–10%,ERROR 全量 |
| 上下文 | 打印「输入参数摘要 + 关键分支 + 结果」,不打印整个对象 |
判据:拿到一个 trace_id,能在 1 分钟内串起这次请求的所有服务日志——日志才算合格。
五、链路追踪:一次请求的地图
| 要点 | 做法 |
|---|
| 贯穿 | 入口生成 trace_id,通过 HTTP Header / 消息属性透传到所有下游 |
| 采样 | 正常流量 1%–10% 采样;错误与慢请求强制采样 |
| Span 粒度 | 一次外部调用一个 Span,包含目标服务、耗时、状态 |
| 关键属性 | 接口名、状态码、重试次数、是否命中缓存 |
| 用途 | 定位「慢在哪一段」「错误从哪个服务开始」 |
反模式:每个函数都埋 Span,链路变成毛线团,反而看不清。
六、告警设计:只报症状,不报原因
| 告警类型 | 例子 | 是否叫人 |
|---|
| 症状告警(好) | 下单成功率 < 95% 持续 5 分钟 | 叫,必须处理 |
| 原因告警(差) | 单台机器 CPU > 80% | 进看板,不叫人 |
| 预测告警 | 磁盘 7 天内写满 | 工单,不半夜叫 |
| 事件通知 | 发布完成、扩容完成 | 只记录 |
6.1 一条好告警的五个要素
1. 症状:用户能感知的失败(错误率/延迟/业务成功率)
2. 阈值与窗口:连续 5 分钟超过 1%,而非瞬时抖动
3. 分级:P1 立即叫人 / P2 工作时间处理 / P3 工单
4. 处置:一句话说明先看哪个看板、能否直接回滚
5. 归属:明确的负责团队,而非「所有人」
6.2 减少告警疲劳
| 手段 | 说明 |
|---|
| 多窗口燃烧率 | 1 小时燃尽 2% 预算 → P1;6 小时燃尽 5% → P2 |
| 去重与抑制 | 同一根因只发一条;上游故障时抑制下游告警 |
| 定期清理 | 每月统计「触发了但没人处理」的告警,直接删或改 |
| 目标 | 无效告警占比 < 20%,每条告警都能对应一个动作 |
七、看板分层:不同人看不同的屏
| 层级 | 看什么 | 观众 | 更新 |
|---|
| 业务层 | 下单量、支付成功率、活跃用户、错误预算 | 业务 + 管理层 | 分钟级 |
| 服务层 | RED 指标、依赖健康、队列积压 | 研发 + 运维 | 分钟级 |
| 资源层 | CPU/内存/磁盘/网络/连接池 | 运维 | 分钟级 |
| 发布层 | 版本分布、灰度比例、发布后错误率对比 | 发布负责人 | 实时 |
判据:值班人打开第一屏(业务层)就能判断「现在是否在出事」,不需要点三次以上。
八、业务埋点:技术指标之外的第二只眼
| 埋什么 | 为什么 | 示例 |
|---|
| 关键动作成功/失败 | HTTP 200 不代表业务成功 | 下单成功数、支付成功数 |
| 漏斗节点 | 定位用户在哪一步流失 | 加购 → 下单 → 支付转化 |
| 关键耗时 | 端到端体验 | 首屏时间、下单总耗时 |
| 异常兜底 | 静默失败最危险 | 降级触发次数、缓存击穿次数 |
规范:埋点事件名用 对象_动作(如 order_created),字段稳定,禁止随意改名。
🎓 实战场景(TIL)
- 场景:用户投诉变慢,客服报了才知道。本质:只监控资源水位(CPU/内存),不监控用户体验(延迟、错误、饱和度)。要点:先上黄金四信号(延迟/流量/错误/饱和度),资源指标只做容量预警。
- 场景:日志一搜一大片,仍定位不了具体请求。本质:日志无结构、无关联 ID。要点:一个 trace_id 贯穿网关→服务→队列,日志按结构化字段输出,报错必带请求上下文。
- 场景:告警设「CPU 超 80% 则报警」,真故障时却不报。本质:盯的是原因指标,不是结果指标。要点:用 SLO/错误预算烧穿来驱动报警;资源类指标作为诊断维度下钻,不作为主告警。
- 场景:看板十几块,出事还是靠临场翻。本质:看板按团队划分而非按提问路径。要点:顶层只看健康度,第二屏给分维下钻(接口/依赖/资源),每张图都带「异常时下一步看哪里」。
📋 一页速查
| 我要做 | 照着做 |
|---|
| 给新服务加监控 | 先埋四个黄金信号,再看板 + 一条症状告警 |
| 选指标 | 请求型用 RED,资源型用 USE,一律看 P95/P99 |
| 写日志 | JSON + trace_id + 级别语义化;ERROR 必须可行动 |
| 设计告警 | 只报用户症状;5 分钟窗口;有分级、有处置、有归属 |
| 压告警噪声 | 每月清理「触发无人处理」的规则,目标无效占比 <20% |
| 控制成本 | 指标标签只放低基数字段;日志分级保留;链路采样 |
| 排查故障 | 指标发现 → 链路定位 → 日志取证,禁止直接 grep |
| 看板 | 第一屏业务层,能一眼判断是否在出事 |
🔗 相关链接