用途
无法观测的代码,就无法运维。可观测性是指借助代码发出的 telemetry,从外部回答“系统正在做什么、为什么”。埋点不是上线后的附加项——它和功能代码一起写,就像测试一样。如果一个功能在没有 telemetry 的情况下发布,第一个用户上报的 bug 就变成了考古,而不是查询。
何时使用
- 构建任何会跑在生产环境的功能
- 添加新的服务、endpoint、后台 job 或外部集成
- 一次生产事故的诊断花了太久("we couldn't tell what happened")
- 建立或评审告警规则
- 评审一个新增 I/O、重试、队列或跨服务调用的 PR
不适用于:诊断正在发生的故障(用 debugging-and-error-recovery skill);对已测量出的慢做 profiling 和优化(用 performance-optimization skill);发布日的监控 checklist 和回滚触发器(见 shipping-and-launch skill)。
主要能力
1. 先定义“能工作”,再埋点
没有问题支撑的 telemetry 就是噪音。在添加任何埋点之前,写下 on-call 工程师针对这个功能会问的 2–4 个问题。例如 checkout payment retry 功能:首次支付成功比例、永久失败原因、支付提供商是否变慢。每个信号都必须帮助回答其中一问。
2. 为每个问题选对信号
- 结构化日志:回答“这个具体案例里发生了什么?”,按事件计,随流量增长。
- 指标:回答“聚合来看,多频繁 / 多快?”,每个 series 固定,查询便宜。
- Trace:回答“时间花在了跨服务的哪一段?”,按请求计,通常采样。
经验法则:指标告诉你出了什么问题,trace 告诉你问题在哪里,日志告诉你为什么。
3. 结构化日志
记录事件,不是散文。每一行日志都是一个 JSON object,带有稳定的事件名和机器可读的字段。日志级别:error(不变量被打破,需调查)、warn(降级但已处理,留意趋势)、info(重要业务事件)、debug(诊断细节,生产默认关闭)。
Correlation ID 是强制的。在系统边界生成或接受一个 request ID,并附加到每一行日志、每个 span 和每次对外调用上。当多个入口点写同一份日志时,给日志标出入口点(entryPoint),与 correlation ID 一起传播,而不是在下游推断。
绝不记录 secret、token、密码或完整 PII。按 allowlist 记录字段,不要整包记录 request body。
4. 指标
对 request-driven 的服务,在每个 endpoint 和每个外部依赖上埋 RED:Rate(请求/秒)、Errors(失败率)、Duration(延迟 histogram,不是平均值)。对资源(队列、连接池、主机),用 USE:Utilization、Saturation、Errors。
Cardinality 是故障模式。每个唯一 label 组合都是一条独立 time series。Label 必须来自小而固定的集合(route template、status class、provider 名)。绝不要用 user ID、原始 URL、错误消息或其他无界值做 label。永不追踪平均值,永远追踪百分位:用 histogram,读 p50/p95/p99。
5. 分布式 tracing
用 OpenTelemetry——厂商中立的标准,auto-instrumentation 以近乎零代码覆盖 HTTP、gRPC 和常见 DB client。只在有意义的内部工作单元周围添加手动 span,并附上 on-call 会用来过滤的 attributes。让 context 跨越每一个 async 边界传播。默认以低比例做 head-based 采样;如果后端支持 tail sampling,对错误保留 100%。
6. 告警
对用户能感受到的症状告警,不是对原因。基于原因的告警会在什么都没出错时开火,并错过未预测到的故障。每条告警必须可行动、链接到 runbook、有基于 SLO 或历史数据的阈值和持续时间,只用两个严重级别:page(面向用户,立即行动)和 ticket(降级,本周内行动)。
7. 编写 Runbook
每条告警都链接到一个 runbook,回答三个问题:正在发生什么、先检查什么、如果解决不了该找谁。存放在 docs/runbooks/,以告警命名。最小可用 runbook 三行即可。把更新 runbook 作为关闭每一次用过它的事故的一部分。
8. 验证 telemetry 本身
埋点是代码,它会错。在宣布完工之前,触发那些路径,去看实际输出:在 staging 人为制造错误并用 requestId 找到它;发送测试流量确认 metric series 出现;在 tracing UI 里跟随请求跨服务;每条新告警试发一次。
使用方式
按上述流程逐步执行:定义问题 → 选信号 → 结构化日志 → 指标 → tracing → 告警 → runbook → 验证。文中提供了 Express 中间件、prom-client Histogram、OpenTelemetry NodeSDK 等代码示例,可直接参考。
适用场景
构建生产功能、添加服务或集成、诊断事故、建立告警规则、评审涉及 I/O 或跨服务调用的 PR。
限制
不用于诊断正在发生的故障、性能 profiling 优化、发布日监控 checklist。缺失事实暂未提供。