应用遥测实用指南
应用遥测是软件生成的结构化证据,包括发生的事情、针对谁、花费了多长时间以及结果是否有用。良好的遥测技术可以让产品、工程、支持和运营从同一事件契约中回答问题,而不是从无限制的文本中重建现实。
本指南展示了如何选择正确的信号、设计安全结果事件、交付它们、使用 SQL 验证它们,以及将它们转变为仪表板和告警。
有用的应用程序事件通过存储和 SQL 分析从发出边界保持结构化。
应用遥测包括哪些内容
日志、指标、跟踪和结构化事件重叠,但每个都有一个有用的重心:
| 信号 | 最擅长 | 典型问题 |
|---|---|---|
| 结构化事件 | 持久的业务或应用程序成果 | 哪些账户注册后无法激活? |
| 事件写入 | 详细的诊断记录 | 此过程在一次失败时报告了什么? |
| 公制 | 廉价总体趋势 | 请求量或 CPU 是否发生变化? |
| 踪迹 | 分布式工作的因果路径 | 哪个跨度使该请求变慢? |
不要强迫一个信号取代其他所有信号。终端 api_request_completed 事件可以保存错误率仪表板所需的路线、帐户、状态、持续时间和释放。跟踪可以解释哪个依赖项消耗了该持续时间。诊断日志可以保留经过审查的技术消息。基础设施指标可以显示服务是否受到资源限制。
这些信号之间的共享标识符通常比复制其完整有效负载更有价值。
从一个决定开始
仪器应该从一个问题和一个行动开始:
- 问题:发布后大多数账户的哪些 API 路由失败?
- 决策:回滚、禁用功能或调查依赖性。
- 事件:
api_request_completed。 - Grain:一项已完成的请求。
- 维度:路由模板、方法、状态码、错误类别、发布。
- 测量:延迟(以毫秒为单位)。
- 安全关联:请求和帐户标识符。
如果某个字段不会被已知的过滤器、组、计算、联接、调查或策略使用,请将其保留。 “以后可能有用”会产生成本、隐私风险和不稳定的模式,但不能保证有用的答案。
设计一份活动合同
终端请求事件可能如下所示:
{
"event_id": "evt_api_01",
"request_id": "req_2f71",
"account_id": "acct_8f31",
"route": "/v1/query/:id",
"method": "POST",
"status_code": 200,
"latency_ms": 184,
"error_type": null,
"release": "2026.07.3"
}
合同应载明:
| 财产 | 定义 |
|---|---|
| 活动名称 | 稳定的过去时结果,例如 api_request_completed |
| 谷物 | 正是一行代表的内容 |
| 发射边界 | 应用程序状态发生变化后,结果即为最终结果 |
| 业主 | 负责制作人的团队或服务 |
| 必填字段 | 每个接受的行必须具有的值 |
| 控制值 | 允许的状态、类别、方法或版本 |
| 单位 | _ms、_bytes、_usd 或其他显式后缀 |
| 隐私等级 | 非敏感、假名或需要审查 |
| 保留需求 | 决策需要多长时间的数据 |
使用 /v1/query/:id 等路由模板,而不是将每个标识符转换为新维度的原始 URL。使用有界的 error_type,而不是堆栈跟踪。保持数字测量数字和字符串类型的稳定标识符。
在结果边界处发射
仅在已知结果时才记录结果。对于 HTTP 请求,通常是在最终状态和持续时间可用之后:
import telemetry from "telemetry-sh";
telemetry.init("YOUR_API_KEY");
async function recordRequest(context, response, startedAtMs) {
await telemetry.log("api_request_completed", {
event_id: crypto.randomUUID(),
request_id: context.requestId,
account_id: context.accountId,
route: context.routeTemplate,
method: context.method,
status_code: response.status,
latency_ms: Date.now() - startedAtMs,
error_type: response.error
? classifyRequestError(response.error)
: null,
release: process.env.APP_RELEASE ?? "unknown"
});
}
Telemetry 在标准化期间删除空值,因此成功的行不存储 error_type。查询层提供timestamp_utc;客户端提供的具有该名称的字段将被删除。请参阅 日志API 了解确切的可接受形状和标准化规则。
不要让遥测传输失败将已完成的业务成果更改为重复的付款、电子邮件、工作或请求。根据工作流程的风险决定是否缓冲、重试、采样或丢弃。重试传递同一逻辑事件时,重复使用同一 event_id。
选择关联标识符
使用与正在调查的实体匹配的标识符:
event_id对一个遥测事件进行重复数据删除;request_id链接一次请求的申请证据;job_id连接生命周期事件和重试尝试;account_id衡量客户影响;user_id获批后支持演员级产品分析;trace_id链接到分布式跟踪。
保持标识符为假名。电子邮件地址、访问令牌、会话 cookie、提示、文档或完整 URL 不是方便的标识符;它是敏感的有效负载数据。
控制基数和有效负载大小
基数是字段产生的不同值的数量。高基数 ID 对于调查和联接很有用,但默认仪表板组较差。无限制的文本很少是安全的分析维度。
用途:
route: "/v1/query/:id"代替/v1/query/9be1...;error_type: "upstream_timeout"代替异常消息;- 经批准的提供商模型标识符,而不是临时显示标签;
release: "2026.07.3"而不是完整的部署清单。
对有界域进行分组。仅在受限调查结果中选择标识符。省略请求和响应正文,而不是在摄取后尝试脱敏每个可能的敏感值。
保持类型和含义稳定
事件灵活性不是发送混合类型的理由。 latency_ms 不得在 184、"184 ms" 和 "slow" 之间交替。 account_id 不得引用一项服务中的用户和另一项服务中的组织。
附加可选字段通常是最安全的演变。重命名、单位更改、类型更改或新的行粒度需要迁移或新的事件版本。在更改仪表板或告警之前,请遵循 模式演化指南 并按版本衡量现场采用情况。
嵌套对象可以创建有用的名称空间,但每个点路径仍然是一个契约。参见 查询嵌套JSON。
单独的事件时间和摄取时间
定义业务或应用程序结果发生的时间。延迟的移动客户端、队列、离线代理和重试缓冲区可能会晚于该时刻传送。
对于服务器生成的在线事件,Telemetry的托管timestamp_utc往往是正确的操作查询时间。如果您的工作流程需要源事件时间,请发送单独命名的时区限定时间戳并记录迟到对报告的影响。不要将部分当前存储桶与完整的历史存储桶进行比较。
使用时间戳指南 涵盖 UTC、窗口和迟到数据。
查询第一个有用的问题
从有界样本开始:
SELECT
timestamp_utc,
route,
status_code,
latency_ms,
release
FROM api_request_completed
WHERE timestamp_utc >= now() - INTERVAL '1 hour'
ORDER BY timestamp_utc DESC
LIMIT 100;
然后计算数量、错误率、受影响的帐户和尾部延迟:
SELECT
route,
COUNT(*) AS requests,
SUM(CASE WHEN status_code >= 500 THEN 1 ELSE 0 END) AS errors,
COUNT(DISTINCT CASE
WHEN status_code >= 500 THEN account_id
END) AS affected_accounts,
100.0 * SUM(CASE WHEN status_code >= 500 THEN 1 ELSE 0 END)
/ NULLIF(COUNT(*), 0) AS error_rate_pct,
approx_percentile_cont(latency_ms, 0.95) AS p95_latency_ms
FROM api_request_completed
WHERE timestamp_utc >= now() - INTERVAL '24 hours'
GROUP BY route
HAVING COUNT(*) >= 20
ORDER BY affected_accounts DESC, error_rate_pct DESC;
最小量规则可以防止单个安静故障的排名自动超过繁忙的回归。关键的小批量工作流程可能需要自己的告警,而不是通用排行榜。
构建决策就绪仪表板
应用程序可靠性仪表板应该让读者从检测转向范围和证据:
- 请求或工作流程量;
- 完整时间范围内的成功率或错误率;
- p50 和 p95 潜伏期;
- 受影响的账户;
- 按路线、错误类别和发布进行细分;
- 包含用于调查的请求 ID 的受限表。
注释单位、时间范围、分母、最小数量、数据新鲜度和事件契约。在重要的 SQL 旁边保留合成结果或已知装置,以便审阅者可以知道查询打算返回什么。
使用 API 可靠性仪表板示例 和 API 延迟配方 作为完整的起点。
仅在所有者可以响应时发出告警
告警定义需要查询、精确分母、完整时间窗口、阈值或基线、最小数量、所属团队、运行手册、首次诊断故障以及丢失数据的行为。
例如:当 p95 延迟超过三个完整存储桶的路由目标并且观察到至少 100 个请求时,通知 API 所有者。响应首先比较版本、错误类别和受影响的帐户计数。
监视遥测管道本身。平零可能意味着安静的应用程序、损坏的生产者、交付失败或查询错误。 遥测传送事件架构 和 摄入新鲜度配方 使缺失的证据可见。
保护隐私和安全
收集决策所需的最少证据。发布前:
- 对每个标识符和自由文本字段进行分类;
- 删除机密、凭据、cookie、请求正文、提示和生成的内容;
- 使用假名内部 ID;
- 限制暴露参与者或资源标识符的调查视图;
- 根据记录的运营或产品需求设置保留;
- 测试脱敏和失败分支,而不仅仅是成功的请求;
- 审查数据管辖权和使用的删除和访问要求。
验证整个路径
生产者单元测试是必要的,但还不够。验证:
- 成功、失败、超时、重试和重复分支;
- 字段名称、类型、单位和控制值;
- API验收及错误处理;
- 目标表中最近的原始行;
- 与已知夹具的总和 SQL;
- 仪表板的时间窗口和分母;
- 告警的阈值、所有者和缺失数据行为;
- 先前生产者版本的回滚路径。
按部署后的发布跟踪现场覆盖范围和事件量。源代码中存在的代码路径并不能证明生产正在发出完整的事件。
控制成本而不损失结果
在广泛推出之前估计数量:
events per day
= requests per day
× events per request
× retained sample fraction
当不使用中间状态时,优先选择一个最终结果事件而不是多个冗余进度事件。仅在保留速率所需的分母后才对大容量成功诊断进行采样。在政策允许的情况下保留故障和罕见的关键结果,但不要使用采样来替代删除敏感数据。
将保留期与拥有记录所有者的最长比较或调查窗口保持一致。在成为永久成本之前,不应将任何人质疑的字段或事件从计划中删除。
应用程序遥测部署清单
- 写下问题、决定、负责人和预期响应。
- 定义一行的粒度和确切的结果边界。
- 选择必填字段、受控值、单位和安全标识符。
- 对隐私、基数、保留和数量进行分类。
- 添加代表性的成功、失败、重试、复制和回滚装置。
- 仪器交付无需改变业务操作语义。
- 按版本验证接受的行和必填字段覆盖率。
- 根据已知结果测试 SQL。
- 发布仪表板定义、新鲜度和分母。
- 仅当所有者和响应明确时才添加告警。
- 监视遥测管道本身。
- 在第一个保留窗口之后查看字段并删除未使用的数据。
继续使用 设计事件架构、结构化日志记录、事件模式目录 和 端到端 SaaS 可观测性演示。