2026 年 5 月 21 日,在明尼阿波利斯的 Observability Summit 上,CNCF 宣布 OpenTelemetry 正式毕业。从 2019 年 OpenTracing 与 OpenCensus 合并立项,到如今成为 CNCF 最高等级的毕业项目,OTel 走了将近七年。毕业不是终点——它意味着项目已经在大规模生产环境中站稳了脚,API 稳定、社区成熟、供应商生态齐全,可以放心地作为可观测性的默认选择。
为什么毕业这件事值得关注
CNCF 的毕业门槛并不低:项目需要证明有多个独立组织在生产环境大规模使用、有完善的治理流程、有稳定的 API 和明确的发布节奏。OpenTelemetry 满足这些条件的方式不是靠某一家公司推,而是靠事实上的行业共识——几乎所有主流可观测性后端(Datadog、Grafana、New Relic、Splunk、Elastic、Jaeger、Prometheus……)都已经接入 OTel 协议或直接使用 OTel SDK。
对开发者来说,毕业带来的实际变化是:
- API 不会频繁变动了。Trace API、Metrics API、Logs API 已经进入稳定阶段,你可以放心在核心服务里引入,不用担心下个版本大改接口。
- 跨语言体验趋于一致。Java、Python、Go、Node.js、.NET、Rust 等十余种语言的 SDK 覆盖率和行为一致性大幅提升,团队不再需要为每种语言写不同的埋点逻辑。
- 供应商锁定风险进一步降低。OTel 是 vendor-neutral 的标准,换后端只需要改 exporter 配置,不需要改业务代码里的埋点。
OTel 到底给了你什么
OpenTelemetry 的核心是三根支柱:Traces、Metrics、Logs。但它真正解决的问题不是"采集这三样东西",而是统一采集方式。
过去,每个可观测性供应商都有自己的 SDK 和数据格式。一个服务用了 Datadog 的 SDK 做链路追踪,另一个服务用了 Jaeger,数据就拼不起来。OTel 的做法是:
- 定义统一的语义约定(Semantic Conventions):
http.request.method、k8s.pod.name这些属性名是固定的,不管后端是谁,数据含义一致。 - 提供统一的 SDK:业务代码只跟 OTel API 打交道,具体数据发到哪里由配置决定。
- 提供 Collector 作为通用中转:接收、处理、导出,一个管道搞定。
实战:在 Python 服务中接入 OpenTelemetry
下面是一个最小但完整的示例,展示如何在 Python FastAPI 服务中接入 OTel,把链路追踪数据发送到 Collector。你可以直接复制运行,然后根据需要替换后端。
1. 安装依赖
pip install fastapi uvicorn opentelemetry-api opentelemetry-sdk \
opentelemetry-instrumentation-fastapi \
opentelemetry-exporter-otlp
2. 最小可运行的 FastAPI 服务 + OTel 埋点
# app.py
from fastapi import FastAPI
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.resources import Resource
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
# 创建 Resource,标识这个服务
resource = Resource.create({
"service.name": "my-fastapi-service",
"service.version": "0.1.0",
"deployment.environment": "dev",
})
# 设置 TracerProvider
provider = TracerProvider(resource=resource)
# OTLP Exporter:数据发到本地 Collector(默认 gRPC 端口 4317)
exporter = OTLPSpanExporter(endpoint="http://localhost:4317", insecure=True)
provider.add_span_processor(BatchSpanProcessor(exporter))
trace.set_tracer_provider(provider)
# 创建 FastAPI 应用并自动埋点
app = FastAPI()
FastAPIInstrumentor().instrument_app(app)
@app.get("/hello")
def hello():
tracer = trace.get_tracer("my-fastapi-service")
with tracer.start_as_current_span("do-something-interesting") as span:
span.set_attribute("custom.detail", "just a demo")
return {"message": "hello, observability!"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
3. 启动一个本地 Collector 接收数据
用 Docker 跑一个最小 Collector,把收到的 trace 打印到控制台(生产环境会换成导出到 Jaeger、Grafana Tempo 等):
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
exporters:
debug:
loglevel: debug
service:
pipelines:
traces:
receivers: [otlp]
exporters: [debug]
docker run --rm -p 4317:4317 -p 4318:4318 \
-v $(pwd)/otel-collector-config.yaml:/etc/otelcol/config.yaml \
otel/opentelemetry-collector:0.96.0
4. 运行并验证
# 启动服务
python app.py
# 发请求
curl http://localhost:8000/hello
# 在 Collector 容器日志中你应该能看到完整的 span 数据
改后端只需要改 Collector 的 exporters 配置——比如加一个 otlp/jaeger 或 prometheus——业务代码一行不动。这就是 vendor-neutral 的实际含义。
采纳建议与注意事项
OpenTelemetry 毕业了,但采纳路径仍然需要根据团队现状来选。几个实际建议:
先从 Trace 入手。Traces 的业务价值最直观——一个请求跨三四个服务的链路,没有统一 trace 根本拼不起来。Metrics 和 Logs 可以逐步加入,OTel 的 Logs API 稳定较晚,不急。
Collector 不要跳过。有些团队直接把 SDK 的 exporter 指向后端,省掉 Collector。短期可以,但长期你会失去缓冲、过滤、属性富化的能力。Collector 是 OTel 架构里最灵活的组件,建议一开始就部署。
注意性能开销。BatchSpanProcessor 是推荐选项,它异步批量发送 span,对请求延迟影响很小。不要用 SimpleSpanProcessor(每请求同步发送),除非是调试场景。
语义约定要遵守。OTel 定义了 http.request.method、db.system 等标准属性名。自己起名字(比如 requestType)会让后端无法自动识别,降低数据价值。先查 Semantic Conventions,再决定是否自定义。
渐进式采纳清单:
| 步骤 | 内容 | 预期耗时 |
|---|---|---|
| 1 | 在 1-2 个核心服务加 OTel Trace SDK + Collector | 1-2 天 |
| 2 | 验证链路能完整拼出来,确认后端接收正常 | 半天 |
| 3 | 所有新服务默认引入 OTel SDK(CI 模板里加好) | 1 天 |
| 4 | 旧服务逐步补埋点,优先覆盖关键路径 | 按服务数量 |
| 5 | 加入 Metrics(OTel SDK 或 Prometheus exposition) | 1-2 周 |
| 6 | 加入 Logs,统一通过 Collector 转发 | 1-2 周 |
OpenTelemetry 毕业意味着它不再是"值得关注的新项目",而是"应该默认使用的标准"。如果你还在为不同后端维护不同的埋点代码,现在是可以开始清理的时候了。