mcpField
O
observability-and-instrumentation

observability-and-instrumentation

github.com/afonsoft开发1345 次安装2 天前更新
代码仓库

关于 observability-and-instrumentation

Observability and Instrumentation 是一项软件开发技能,指导团队在生产功能开发时同步规划遥测。内容涵盖结构化日志、指标、分布式追踪、可执行告警和验证,并强调回答运维问题及控制敏感数据与指标基数。

主要功能

  1. 01将遥测信号对应到值班问题
  2. 02指导结构化日志与关联 ID
  3. 03说明 RED 和 USE 指标实践
  4. 04涵盖 OpenTelemetry 分布式追踪
  5. 05制定可执行告警与运行手册要求
  6. 06通过测试场景验证遥测

适用场景

  1. 01为生产功能添加可观测性
  2. 02为新服务或外部集成添加监测
  3. 03在代码审查中检查遥测与告警

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/afonsoft/skills --skill observability-and-instrumentation

安装后在 Claude Code 中输入/observability-and-instrumentation确认已加载。

附带文件references/observability-checklist.md

详细介绍

可观测性与仪表化

概述

无法观测的代码就无法运维。可观测性是指通过代码发出的遥测数据,从外部回答“系统正在做什么,为什么”的能力。仪表化不是发布后的附加项,而是与功能一同编写,就像测试一样。如果功能发布时没有遥测数据,用户报告的第一个 Bug 就会变成考古,而不是查询。

适用场景

  • 构建任何将在生产环境运行的功能
  • 添加新的服务、端点、后台任务或外部集成
  • 生产事故诊断耗时过长(“我们无法判断发生了什么”)
  • 设置或审核告警规则
  • 审核添加了 I/O、重试、队列或跨服务调用的 PR

不适用于:

  • 诊断当前正在发生的故障——请使用调试技能(可观测性让下次调试更快)

  • 分析并优化已测得的性能瓶颈——请使用性能优化技能

  • 发布日监控清单和回滚触发条件——请使用发布/上线技能;本技能涵盖为它们提供数据的仪表化

  • 用户以英语请求或提及此技能(例如,“use /observability-and-instrumentation”、“run observability-and-instrumentation”)。

  • 用户以葡萄牙语请求或提及此技能(例如,“use /observability-and-instrumentation”、“execute observability-and-instrumentation”)。

流程

1. 在添加仪表化之前,定义“正常运行”

没有问题要回答的遥测只是噪声。在添加任何仪表化之前,先写下值班工程师会针对此功能提出的 2–4 个问题:

FEATURE: checkout payment retry
QUESTIONS ON-CALL WILL ASK:
1. What fraction of payments succeed on first attempt vs after retry?
2. When a payment fails permanently, why? (provider error? timeout? validation?)
3. Is the payment provider slower than usual?
→ Every signal below must help answer one of these.

如果你无法说出这些问题,就还没准备好添加仪表化——你只会记录一切,却一无所获。

2. 为每个问题选择合适的信号
信号能回答的问题成本特征示例
结构化日志“这个具体案例中发生了什么?”按事件计费;随流量增长带有提供商错误代码的 payment_failed
指标“总体上发生的频率有多高/速度有多快?”每个序列成本固定;查询开销低提供商调用的 p99 延迟
追踪“时间都花在了哪些服务上?”按请求计费;通常会采样一个缓慢的结账流程,按调用环节拆分

经验法则:指标告诉你是否出了问题,追踪告诉你哪里出了问题,日志告诉你为什么出了问题。

3. 结构化日志

记录事件,而不是散文。每条日志行都是一个 JSON 对象,包含稳定的事件名称和机器可读字段:

// BAD: string interpolation — unqueryable, inconsistent
logger.info(`Payment ${id} failed for user ${userId} after ${n} retries`);

// GOOD: stable event name + structured fields
logger.warn({
  event: 'payment_failed',
  paymentId: id,
  provider: 'stripe',
  errorCode: err.code,
  attempt: n,
}, 'payment failed');

日志级别——请保持一致:

级别含义值班处理
error不变量遭到破坏;可能需要有人采取行动调查
warn服务降级但已处理(重试成功、使用了回退方案)关注趋势
info重要业务事件(订单已下单、任务已完成)无
debug诊断细节默认在生产环境中关闭

关联 ID 是必需的。 在系统边界生成(或接收)请求 ID,并将其附加到每条日志、每个跨度和每次出站调用中。没有它,就无法从交错的日志中还原单个请求:

// Express: child logger per request, ID propagated downstream
app.use((req, res, next) => {
  req.id = req.headers['x-request-id'] ?? crypto.randomUUID();
  req.log = logger.child({ requestId: req.id });
  res.setHeader('x-request-id', req.id);
  next();
});

切勿记录密钥、令牌、密码或完整的个人身份信息(PII)。 遥测管道是常见的数据泄露途径。请使用字段允许列表;不要记录整个请求正文。完整的安全检查清单请参见 ../code-review-and-quality/references/security-checklist.md。

4. 指标

对于由请求驱动的服务,在每个端点和每个外部依赖项上都进行 RED 指标监测:Rate(请求数/秒)、Errors(失败率)、Duration(延迟直方图,而非平均值)。对于资源(队列、池、主机),使用 USE:Utilization(利用率)、Saturation(饱和度)、Errors(错误)。

与追踪一样,与供应商无关的方案是 OpenTelemetry 指标 API(使用与步骤 5 相同的 SDK 和上下文)。下面的示例使用 Prometheus 的 prom-client——这是常见的后端选择之一,并非唯一选择;无论选择哪种后端,RED/USE 和基数规则都相同。

import { Histogram } from 'prom-client';

const httpDuration = new Histogram({
  name: 'http_request_duration_seconds',
  help: 'HTTP request duration',
  labelNames: ['method', 'route', 'status_class'],  // '2xx', not '200'
  buckets: [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5],
});

**基数是故障的根源。**每种唯一的标签组合都是一个独立的时间序列。标签必须来自范围较小且固定的集合(路由模板、状态类别、提供商名称)。切勿将用户 ID、原始 URL、错误消息或其他无界值用作标签——这些内容应记录在日志和追踪中。

OK as label:    route="/api/tasks/:id"   status_class="5xx"   provider="stripe"
NEVER a label:  user_id, email, request_id, full URL, error message text

永远不要追踪平均值,始终关注百分位数:平均值会掩盖那 1% 遇到糟糕体验的用户。使用直方图并查看 p50/p95/p99。

5. 分布式追踪

使用 OpenTelemetry——它是与供应商无关的标准,而且自动插桩几乎不需要编写代码,就能覆盖 HTTP、gRPC 和常见的数据库客户端:

// tracing.ts — must be imported before anything else
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';

const sdk = new NodeSDK({
  serviceName: 'checkout-service',
  instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();

只在有意义的内部工作单元(例如 applyDiscounts、chargeProvider)周围添加手动 Span,并附上值班人员会用来筛选的属性。在每个异步边界传递上下文——例如 HTTP 标头、队列消息元数据——否则追踪会在断点处中断。默认使用低采样率的基于头部采样;如果后端支持基于尾部的采样,则保留 100% 的错误。

6. 告警

针对用户能感受到的症状告警,而不是针对原因告警:

SYMPTOM (page-worthy):           CAUSE (dashboard, not a page):
error rate > 1% for 5 min        CPU at 85%
p99 latency > 2s                 one pod restarted
queue age > 10 min               disk at 70%

基于原因的告警会在一切正常时触发,也会遗漏你未预料到的故障。基于症状的告警则会在用户确实受到影响时触发,无论原因是什么。

每条告警都必须符合以下规则:

  1. **必须可采取行动。**如果响应是“忽略它,它会自行恢复”,就删除这条告警。
  2. 链接到运行手册——即使只有三行也可以:说明告警含义、要运行的第一个查询、升级处理路径。
  3. 阈值和持续时间必须有依据,依据应来自 SLO 或历史数据,而不是猜测。
  4. 只使用两种严重级别:页面告警(面向用户,立即处理)和工单(服务降级,本周内处理)。第三个级别只会带来噪声,让人习惯于忽略所有告警。
7. 验证遥测本身

插桩也是代码,也可能出错。在宣布工作完成之前,触发相关路径并检查实际输出:

  • 在预发布环境中强制触发错误 → 按 requestId 在日志中找到该错误,并确认字段是结构化的(而不是 [object Object])
  • 发送测试流量 → 确认指标序列以预期标签出现,且数值合理
  • 在追踪界面中跟踪一个跨服务的请求 → 确认没有中断的 Span
  • 逐一触发每条新告警(暂时调低阈值)→ 确认告警发送到了正确的渠道,且运行手册链接有效

常见借口

借口现实
“等功能正常运行后再添加日志”“之后”往往会变成“第一次发生事故之后”,而那是发现自己毫无观测能力时成本最高的时刻。边构建边添加监测。
“日志越多,越容易观测”无结构的噪声会让事故处理变慢,而不是更快。三个可查询事件胜过三百行描述性日志。
“暂时用 console.log 就行”无结构的输出无法筛选、关联或设置告警。使用结构化日志记录器只需额外花五分钟。
“出问题时看仪表板就行”如果没有明确的问题作为设计依据,仪表板展示的内容会包罗万象,唯独没有答案。从值班问题入手。
“所有重要事项都设置告警,以后再调”嘈杂的告警会让人们学会忽略它。调整永远不会发生,真正的告警却会被漏掉。
“用用户 ID 作为指标标签更方便调试”这也会让你的指标后端不堪重负。高基数查询应放在日志和追踪中。
“我们只有两个服务,用追踪太小题大做了”两个服务就已经会产生日志无法解答的跨服务延迟问题。自动埋点会让成本微不足道。

危险信号

  • 一个包含重试、队列或外部调用的功能 PR,却没有新增任何遥测数据
  • 使用字符串插值构建日志行,而不是使用结构化字段
  • 没有关联 ID/请求 ID——每条日志行都是孤立的
  • 指标使用用户 ID、原始 URL 或错误消息文本作为标签(基数炸弹)
  • 延迟只追踪平均值,没有百分位数
  • 告警每天都会触发,人们确认后却不采取行动
  • 对 CPU、内存等原因设置告警并通知人员,却没有监控面向用户的错误率
  • 日志中出现密钥、令牌或完整请求正文
  • 生产功能是否正常的唯一依据是“在我的机器上能运行”

验证

为功能添加埋点后,请确认:

  • 已记录该功能的值班问题,并且每个问题都对应一项信号
  • 所有日志输出均为结构化格式(JSON),具有稳定的事件名称,且每行都包含关联 ID
  • 任何日志行中都没有密钥、令牌或未脱敏的 PII(抽查实际输出)
  • 每个新增端点和每个外部依赖都有 RED 指标,且标签集合有界
  • 延迟使用直方图;可以查询 p95/p99
  • 在追踪 UI 中可以端到端跟踪单个请求,且没有断裂的 span
  • 每个新告警都基于症状,带有运行手册链接,并且至少测试触发过一次
  • 在预发布环境中模拟的故障仅通过遥测数据就能定位,无需阅读源代码

如需快速浏览此列表,包括发布前的埋点检查关卡,请参阅 references/observability-checklist.md。

评论

支持 Markdown