一个真实场景
凌晨三点,支付成功率从 99.9% 掉到 92%。你盯着 Grafana 仪表盘——CPU 正常、内存正常、QPS 正常。然后:
指标告诉你「掉了」,但不告诉你谁失败、为什么失败。你要翻过 12 个微服务的日志,手动按 timestamp 拼接上下游调用——两个小时后才发现是下游风控服务某个 IP 被限流。
如果你有分布式追踪,流程会是:打开 Jaeger,筛 http.status_code=500 的 trace,随便点一条——整条调用链 A→B→C→D 时序图一目了然,D 上有红色 span 写着 rate_limited。2 分钟定位。
这就是「监控」与「可观测性」的区别。
监控 vs 可观测性
| 监控(Monitoring) | 可观测性(Observability) | |
|---|---|---|
| 问题 | 已知问题(known unknowns) | 未知问题(unknown unknowns) |
| 方式 | 预设告警阈值、仪表盘 | 在线探索、多维过滤、钻取 |
| 代表 | Prometheus + Alertmanager | 分布式追踪 + 指标 + 日志关联 |
| 适用 | 系统固定、故障模式可枚举 | 微服务、动态、突发故障 |
| 代价 | 低(少量数据) | 高(全量观测数据) |
监控没被可观测性取代——它是可观测性的子集。生产上你两者都要:阈值告警用 metrics,出事了用 trace/log 查根因。
三支柱:Logs / Metrics / Traces
2026-05-06 10:23:41 ERROR payment failed user_id=123。最灵活,信息量大,但数据量也大,查询慢。适合记录发生了什么的详细上下文。http_requests_total{status="500"} = 42。存储廉价、查询飞快、适合告警。但丢失了单个请求的细节——你只知道「掉了」,不知道「谁掉了」。Trace = Span(A) → Span(B) → Span(C)。让你看懂一次调用在分布式系统里经过了什么。三者互补:指标告诉你「出事了」,追踪告诉你「在哪里出事」,日志告诉你「出了什么事」。OpenTelemetry 的伟大之处是把这三者用同一套 API、同一种协议(OTLP)、同一组 trace_id串起来。
一图看懂三者协作
┌─────────────────────────────────────────────────────┐ │ Metrics:告警触发 │ │ http_requests_total{status="500"} 突增 ▲ │ │ │ │ │ ▼ 点开关联的 trace_id │ │ Traces:定位到慢/错的调用链 │ │ trace_id=abc123 │ │ ├─ API Gateway 50ms ✓ │ │ ├─ Payment svc 3s ✗ 500 │ │ │ └─ Risk svc 2.9s ✗ rate_limited │ │ │ │ │ ▼ 点开 span 关联的日志 │ │ Logs:看具体错误信息 │ │ [risk-svc] trace_id=abc123 IP 1.2.3.4 blocked │ └─────────────────────────────────────────────────────┘
关键:这三层之间用 trace_id 做纽带,在 Grafana / Datadog / Jaeger 里一键跳转——这才是现代可观测性的样子。
OTel 之前:乱战十年
2010-2018 年,追踪领域是两派开源标准在打架:
datadog. 前缀,想换就得重写。2019 年春,两边核心维护者坐下来:OpenTracing + OpenCensus = OpenTelemetry。CNCF 把它接过来,从此再没有二选一。
OpenTelemetry 的愿景
用一套供应商中立的 API,生成标准格式的观测数据(OTLP),让你自由选择后端。换后端 = 改 Collector 一行配置,代码零改动。
这意味着:
- 代码层:只依赖
@opentelemetry/api(或各语言等价包),埋一次点 - 传输层:OTLP 协议(Protobuf over gRPC/HTTP)是事实标准
- 后端层:Jaeger、Prometheus、Loki、Tempo、Datadog、New Relic、Honeycomb、Grafana Cloud...... 随你接
- 切换成本:近乎零——只要改 Collector 的
exporters配置
一行代码看懂 OTel
// Node.js 里创建一个 span import { trace } from "@opentelemetry/api"; const tracer = trace.getTracer("my-service"); async function checkout(userId: string) { return tracer.startActiveSpan("checkout", async (span) => { span.setAttribute("user.id", userId); try { const result = await chargeCard(userId); span.setStatus({ code: 1 }); // OK return result; } catch (e) { span.recordException(e as Error); span.setStatus({ code: 2 }); // ERROR throw e; } finally { span.end(); } }); }
这段代码不绑定任何厂商。Collector 配置一改,数据就飞到 Jaeger 或 Datadog——你都不需要重新部署。
CNCF 的位置
2021 年 OpenTelemetry 成为 CNCF Incubating 项目,2023 年升级到 Graduated(毕业级,和 K8s 一个级别)。
- 按 贡献速度排名:OpenTelemetry 长期稳居 CNCF 第二,仅次于 Kubernetes
- 按 企业采用:AWS、Google、Microsoft、Datadog、Splunk、New Relic、Honeycomb 全员参与
- 按 语言覆盖:40+ 语言 SDK,JavaScript/Python/Java/Go/Rust/C++ 都是一等公民
- 按 行业标准:AWS X-Ray、Azure Monitor、GCP Cloud Trace 都原生支持 OTLP
项目组织:规范 vs 实现
OTel 内部分两条线:
| 规范组(Specification) | 实现组(Implementation) |
|---|---|
| 定义 API 接口形状 | 为每门语言实现 SDK |
| 定义 OTLP 协议(Protobuf) | 实现 exporters / instrumentations |
| 定义语义约定(SemConv) | 保持与 spec 版本一致 |
文档在 open-telemetry/opentelemetry-specification | opentelemetry-js / -python / -java / -go ... |
这种分工保证了跨语言一致性——你在 Node.js 埋的 span,Go 服务接收后 trace_id 拼得上,attributes 语义对得齐。
组件全貌(先混个脸熟)
应用代码 │ 调用 ▼ @opentelemetry/api // 厂商中立的接口(trace/metrics/logs) │ 被注入 ▼ @opentelemetry/sdk // 具体实现:采样、处理、导出 │ OTLP ▼ OTel Collector // 独立进程:接收 / 转换 / 路由 │ OTLP / Jaeger / Prom ... ▼ 后端(Jaeger / Prom / Datadog)
后续章节会逐层拆解——第 2-4 章讲 API/SDK 层(Traces/Metrics/Logs),第 5 章讲 Context 怎么跨越进程,第 6 章讲自动埋点,第 7 章讲 Collector,第 8 章讲语义约定,第 9 章讲后端选型,第 10 章是生产实战 + LLM 可观测性。
为什么 2026 是入坑好时机
· Traces 规范 Stable(1.0 发布于 2021)
· Metrics 规范 Stable(2023 达成)
· Logs 规范 Stable(2024 达成)
· 40+ 语言 SDK 基本全 GA
· GenAI 语义约定(OpenLLMetry)2025 年稳定,LLM 可观测性进入主流
2026 的状态是:你现在学的东西,三年内不会推翻重来。而且越来越多的开源库(Bun、Deno、Hono、tRPC、LangChain)都内建了 OTel 支持——不学就只能看别人的 trace 看不懂。
本章要记住的 5 件事
- 可观测性 ≠ 监控。监控回答已知问题,可观测性解决未知问题
- 三支柱 Logs/Metrics/Traces 各有分工,靠
trace_id串联 - OTel = OpenCensus + OpenTracing 合并,CNCF Graduated 项目
- 分为 Spec(接口/协议/语义) 和 Impl(各语言 SDK) 两条线
- 核心价值:供应商中立——埋一次点,任意切后端