关于 opentelemetry
介绍 OpenTelemetry 核心实践的技能,涵盖信号选择、Span 设计、上下文传播、采样和 OTLP 导出。适合实现或排查跨服务遥测的开发者,并说明常见埋点错误。
主要功能
- 01追踪、指标与日志的选择
- 02Span 命名、属性、事件和错误处理
- 03W3C 上下文传播
- 04采样策略指导
- 05OTLP 导出器配置与常见问题
适用场景
- 01为应用选择合适的遥测信号
- 02设计分布式请求的 Span
- 03排查追踪上下文中断或导出缺失
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mizchi/ skills --skill opentelemetry安装后在 Claude Code 中输入/opentelemetry确认已加载。
附带文件README.md
详细介绍
OpenTelemetry — 核心模式
信号选择
编写代码前,先选择合适的信号:
| 信号 | 适用场景 | 成本 |
|---|---|---|
| Traces | 请求生命周期、延迟归因、分布式因果关系 | 高(每个请求) |
| Metrics | 聚合计数、速率、直方图——仪表板和告警 | 低(预先聚合) |
| Logs | 带上下文的离散事件——错误、审计、调试 | 中 |
经验法则:指标回答“频率有多高/速度有多快”,日志回答“发生了什么”,追踪回答“为什么”。指标足以解决问题时,不要使用追踪。
Span 设计
命名约定
<verb> <noun> → "fetch user", "send email"
<provider>.<operation> → "db.query", "http.get", "cache.set"
切勿在 span 名称中放入可变数据(ID、值)——请改用属性。Span 名称是追踪索引中的基数键。
属性
span.setAttributes({
"db.system": "sqlite",
"db.operation": "select",
"db.sql.table": "users",
"user.id": userId, // high-cardinality: OK as attribute, not in name
});
请遵循 OpenTelemetry 语义约定 中广为人知的属性名称(http.*、db.*、rpc.* 等)——后端和 APM 都会依赖这些名称。
事件与子 Span
- 事件:当前操作中的瞬时点(
span.addEvent("cache_miss")) - 子 Span:有自己的持续时间,延迟很重要,适合用于追踪瀑布图
状态与异常
span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
span.recordException(err); // adds exception.type / exception.message / exception.stacktrace
发生错误时,两者都要调用。单独调用 recordException 不会将状态切换为 ERROR——Span 在界面中仍会显示为成功。
确保调用 span.end()
// ✓ callback form guarantees end()
tracer.startActiveSpan("operation", (span) => {
try {
return doWork();
} catch (e) {
span.recordException(e as Error);
span.setStatus({ code: SpanStatusCode.ERROR });
throw e;
} finally {
span.end();
}
});
未结束的 Span 会滞留在处理器队列中,可能永远不会导出。
上下文传播
W3C TraceContext(traceparent / tracestate)是标准规范。在传出请求时注入,在传入请求时提取:
import { propagation, context } from "@opentelemetry/api";
// Outgoing HTTP
const carrier: Record<string, string> = {};
propagation.inject(context.active(), carrier);
fetch(url, { headers: carrier });
// Incoming (server handler)
const ctx = propagation.extract(context.active(), request.headers);
tracer.startActiveSpan("handle request", { context: ctx }, (span) => {
// span is now a child of the upstream trace
});
最常见的错误:创建 Span 时没有提取传入上下文 → 追踪瀑布图会断裂成互不相连的根 Span。启动根服务器 Span 之前,务必先提取上下文。
采样
| 策略 | 使用时机 |
|---|---|
AlwaysOn | 开发环境/低流量预发布环境 |
TraceIdRatioBased(0.1) | 高流量生产环境——对 10% 的新追踪进行采样 |
ParentBased(root: TraceIdRatioBased) | 推荐的默认策略——遵循上游决策,并按比例对新的根 Span 采样 |
| 尾部采样(OTel Collector) | 无论头部采样决策如何,都需要完整保留所有错误 |
ParentBased 可避免上游对追踪进行采样、下游却将其丢弃的故障情形(瀑布图断裂)。
OTLP 导出器配置
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
const exporter = new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT + "/v1/traces",
headers: { Authorization: `Bearer ${process.env.OTEL_API_KEY}` },
});
provider.addSpanProcessor(
new BatchSpanProcessor(exporter, {
maxQueueSize: 512,
scheduledDelayMillis: 2000,
exportTimeoutMillis: 10_000,
})
);
生产环境中请使用 BatchSpanProcessor——它是异步的,开销也很低。SimpleSpanProcessor 会阻塞事件循环;仅用于本地调试。
常见问题
- 未调用
provider.register():SDK 已配置,但没有任何内容被导出。请在任何插桩运行前调用。 - ESM + 自动插桩(Node.js):
require-in-the-middle钩子不会对 ESM 静态导入触发。解决方法请参阅otel-node。 - Cloudflare Workers:没有 Node.js 运行时,需要进行 fetch 边界插桩。请参阅
cloudflare-workers-otel-utels。 - Span 名称包含动态数据:会导致追踪索引基数激增。请改用属性。
- 出站调用未使用 W3C 传播:分布式追踪会断裂——下游 Span 会显示为孤立的根 Span。
评论
支持 Markdown