Development
可观测性与埋点
为代码添加埋点,使生产环境中的行为可见且可诊断。在添加日志、指标、链路追踪或告警时使用。
无法观测的代码就是无法运维的代码。埋点应当与功能一同编写,而不是上线后再补充。
在添加任何埋点之前,先写下一名值班工程师将会提出的 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?
3. Is the payment provider slower than usual?
→ Every signal below must help answer one of these.
| 信号 | 回答的问题 | 成本特征 | |---|---|---| | 结构化日志 | “这个具体场景里发生了什么?” | 按事件计 | | 指标 | “总体上多频繁 / 多快?” | 每序列固定 | | 链路追踪 | “时间消耗在跨服务的哪一环?” | 按请求计 |
指标告诉你出了问题,链路追踪告诉你问题出在哪里,日志告诉你为什么。
记录事件,而非散文:
// BAD: string interpolation — unqueryable
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(Correlation ID)是必需的:
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();
});
对于请求驱动的服务,在每个端点上埋设 RED 指标:速率(Rate)、错误(Errors)、时延(Duration)。
import { Histogram } from 'prom-client';
const httpDuration = new Histogram({
name: 'http_request_duration_seconds',
help: 'HTTP request duration',
labelNames: ['method', 'route', 'status_class'],
buckets: [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5],
});
高基数(Cardinality)是失败的根源。 切勿将用户 ID、原始 URL 或错误信息用作标签。
永远不要追踪平均值,始终追踪百分位。
使用 OpenTelemetry——它是厂商中立的标准:
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
const sdk = new NodeSDK({
serviceName: 'checkout-service',
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
针对用户能感受到的症状告警,而非针对原因:
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%
每条告警的规则:
为功能埋点之后: