HelloWorld 链路追踪教程
端到端链路追踪就是把一次请求在多个服务间的执行过程串成一条“时间线”,记录每个片段(span)的开始、结束、时长与上下文信息,方便定位慢点、错误和依赖关系。接下来用一个简单的 HelloWorld 示例,带你从概念到动手:在本地跑 Jaeger,给两个小服务埋点(以 Node.js 为例),查看可视化追踪,并讲清常见坑与实战建议。

先弄清楚这玩意儿到底是什么(用一个比喻)
想象你寄了一个包裹,包裹从你家到目的地要经过好几站(收件站、分拣中心、运输车、末端网点)。链路追踪就是给这个包裹绑上一张记录表:每到一站就打卡,记录时间和发生了什么。把这些打卡信息汇集起来,就能看到整个路程哪段耗时、哪段出问题。把服务调用替换成“站”,每个调用片段就是一个 span,整次业务行为就是一个 trace。
关键概念一页纸速览
- Trace:一次端到端请求的整体视图,由若干个 span 组成,携带同一个 trace id。
- Span:一次具体的执行片段(例如一次 HTTP 请求、数据库查询),有开始/结束时间、时长、属性(attributes)、事件(events)和状态(status)。
- Context Propagation:跨服务传递 trace id/span id 的机制,通常通过 HTTP header(如 W3C Trace Context: traceparent)或自定义 header 实现。
- Sampler:决定哪些 trace 被保留并导出,常见策略有“始终采样”、“按概率采样”、“按关键事务采样”。
- Exporter / Collector:把采集到的 span 发送到后端(如 Jaeger、Zipkin、OTel Collector),后端负责存储与展示。
HelloWorld 实战目标与架构
目标:在本地使用 OpenTelemetry(OTel)为两个小服务(frontend -> backend)打链路埋点,数据导出到 Jaeger,能在 Jaeger UI 中看到端到端 trace。示例以 Node.js/Express 为准,但概念对 Java/Python 等同样适用。
需要准备的东西
- Node.js(>=14),npm / yarn
- Docker:用于快速启动 Jaeger(也可用已部署的 Jaeger/OTel Collector)
- 一个简单的两进程示例:frontend(接收浏览器请求并调用 backend),backend(返回字符串)
步骤 1:在本地跑 Jaeger
用 Docker 一行命令启动 Jaeger(all-in-one,适合开发调试):
docker run –rm -p 16686:16686 -p 14250:14250 -p 14268:14268 jaegertracing/all-in-one:1.41
端口说明表:
| 端口 | 用途 |
| 16686 | Jaeger UI(浏览器访问) |
| 14250 | gRPC 接收(OTLP/gRPC 或 Jaeger gRPC) |
| 14268 | HTTP 提交(Jaeger HTTP 接口) |
步骤 2:为 Node.js app 添加 OpenTelemetry
基本思路:在应用入口处初始化 OTel SDK、注册需要的自动化插件(HTTP、Express)、并配置导出器为 Jaeger(或 OTLP 指向 Collector)。然后按正常方式写业务代码,自动或手动创建 span。
示例依赖(package.json 中)
| 依赖 | 用途 |
| @opentelemetry/sdk-node | OTel Node 一体化 SDK |
| @opentelemetry/instrumentation-http | 自动埋点 HTTP 客户端/服务端 |
| @opentelemetry/instrumentation-express | 自动埋点 Express 路由 |
| @opentelemetry/exporter-jaeger | 把 span 发送到 Jaeger |
示例:tracing.js(初始化)
核心要做的事:
- 创建 NodeSDK(或 TracerProvider)
- 注册要启用的 instrumentations
- 配置 Jaeger 导出器并启动 SDK
(以下是伪代码风格,便于理解)
tracing.js:require(‘@opentelemetry/sdk-node’),new NodeSDK({ traceExporter: new JaegerExporter({endpoint: ‘http://localhost:14268/api/traces’}), instrumentations: [/* http, express */] }).start();
示例:frontend.js 与 backend.js(要点)
- frontend:接收 /hello 请求,调用 backend 的 /api 路由(使用 fetch/axios),返回合并结果。
- backend:简单处理并返回 “world”。两端都加载 tracing.js(确保在 app 代码之前加载 tracing 初始化)。
由于使用了 HTTP 与 Express 的自动化 instrumentations,OTel SDK 会自动创建对应的 server span(接收请求)和 client span(发起到 backend 的 HTTP 请求),并通过标准 header(traceparent)把上下文传下去。
如何在 Jaeger UI 里看懂一条 Trace
打开 http://localhost:16686,选择服务名(frontend、backend),找到最近的 trace。关键点:
- 最顶层是 trace,总耗时显示在左上角。
- 时间线展示每个 span 的开始相对位置与长度(耗时),子 span 一般在父 span 下面。
- 点击某个 span,可以看到 attributes(HTTP 方法、URL、status code 等)、events、以及 trace id/span id。
常见问题与排查思路(实践中经常遇到)
- 看不到 trace:确认应用是否正确加载 tracing 初始化脚本(必须在业务模块 require 之前),检查 exporter 地址和端口,查看 exporter 抛出的日志。
- 上下文丢失:检查是否存在自定义 HTTP 客户端或跨线程调用未传递 context;注意异步边界(例如非 Promise 回调)可能需要手动在 context 中运行函数。
- 采样导致看不到某些请求:本地开发一般设置为始终采样(AlwaysOn),生产环境常用概率采样,先把采样配置调高以便调试。
- 性能担忧:采样能控制开销;自动化 instrumentation 的 CPU 开销通常很小,但大量属性或事件会增加网络与存储压力。
生产环境的进阶要点(不只是跑通)
- OTel Collector:在大规模部署中,建议使用 Collector 做缓冲、批量、过滤与路由再发到 Jaeger、Zipkin、或商业 APM。
- 日志与追踪关联:把 trace id 注入日志上下文(例如在 Structured Logging 或 MDC 中),便于按 trace 回溯日志。
- 语义约定:遵循 OpenTelemetry 的 semantic conventions(http.method、db.statement 等),有助于统一查询与上下文理解。
- 敏感信息控制:避免把 PII 写入 span attributes,必要时用 sampling/filtering 和脱敏策略保护数据。
- 采样策略优化:可以使用保留采样(对错误或关键事务保留)、自适应采样(根据流量和错误率调整)以兼顾成本与可观测性。
简单的诊断清单(遇到问题按顺序排查)
- Tracing 初始化脚本是否最先加载?(必须)
- Exporter 是否能连通到 Jaeger / Collector?检查端口、协议(HTTP/gRPC)。
- 服务之间的 HTTP header 是否被代理/网关篡改或丢弃?(检查 traceparent)
- 采样率是否过低?临时提升为 100% 进行验证。
- 查看应用日志是否有 OTel 的错误信息。
关于命名与粒度的实践建议(能少犯错)
- 给 span 起有意义的名字:用“操作名 + 目标”而非泛泛的“handler”。例如:GET /api/users -> GET /api/users:db-select。
- 仅记录有助于诊断的属性(例如 HTTP status、DB latency、关键 ID),避免无差别记录大量数据。
- 在关键路径手动创建 span(例如复杂业务流程、队列消费),自动化埋点补充但不要完全依赖自动化。
最后,简单回顾一下实操步骤(思路总是最重要)
- 理解 trace/span 与 context propagation 的原理。
- 在入口处初始化 OTel SDK,启用所需 instrumentations。
- 配置 exporter(直接到 Jaeger 或通过 OTel Collector)。
- 跑起 Jaeger,发起请求,打开 UI 查看 trace,逐步调整采样与属性。
好吧,讲得有点像边走边想:如果你刚开始,只要记住三件事——初始化要早、上下文要传、不要一次性写太多属性。跑通一个 HelloWorld 后,你就能把这套思路拓展到更多语言和更复杂的架构里去。随手把 trace id 打到日志里,会让排查变得顺很多。去动手把它跑起来吧,遇到具体问题再逐条排查,上面那些坑是最常见的。