直接上手就能看到 trace,但本地跑不起来或 span 断在 http 调用处,大概率是上下文传播没对齐或导出器配置错端口——不是代码写得不对,而是 node.js 的自动 instrumentations 默认不处理 fetch 或某些第三方 http 客户端。

直接上手就能看到 trace,但本地跑不起来或 span 断在 HTTP 调用处,大概率是上下文传播没对齐或导出器配置错端口 —— 不是代码写得不对,而是 Node.js 的自动 instrumentations 默认不处理 fetch 或某些第三方 HTTP 客户端。
为什么 http 模块能自动埋点,但 axios 或 node-fetch 却没 span?
OpenTelemetry 的 @opentelemetry/auto-instrumentations-node 默认只 patch 标准库的 http、https、fs 等模块。它不识别 axios 内部调用链,除非你显式启用对应插件或手动 wrap。
-
axios需要额外安装@opentelemetry/instrumentation-axios并注册到 SDK -
node-fetch@3+依赖undici,得启用@opentelemetry/instrumentation-undici - 若用
fetch(全局或cross-fetch),必须确保运行时环境支持globalThis.fetch,且 instrumentation 已加载 - 检查
instrumentation.register()是否在require('http')之前执行 —— 加载顺序错会导致 patch 失效
ConsoleSpanExporter 能看到数据,但 Jaeger/Zipkin 里空着
常见原因是端口映射错、协议不匹配或 exporter 初始化太晚。Jaeger all-in-one 默认监听 14268(HTTP)和 6831(UDP),但 OpenTelemetry SDK 默认走 gRPC(4317)或 OTLP HTTP(4318)。
- 用
@opentelemetry/exporter-jaeger时,endpoint 必须是http://localhost:14268/api/traces,不是http://localhost:16686(那是 UI 端口) - 用
@opentelemetry/exporter-zipkin时,endpoint 是http://localhost:9411/api/v2/spans,注意路径后缀 - 若改用 OTLP(推荐),启动 Jaeger 时加
-p4318:4318,exporter 配置为new OTLPTraceExporter({ url: 'http://localhost:4318/v1/traces' }) - 确认 Node.js 进程有网络权限:Codespaces 或 Docker Desktop 下,
localhost指容器内网关,得用host.docker.internal
VSCode 调试时 span 名称全是 anonymous 或漏掉关键属性
这是 tracer 初始化时机和 scope 管理问题。Node.js 的自动 instrumentations 依赖全局 TracerProvider 注册,而 VSCode 的调试器可能在 SDK 启动前就加载了业务模块。
- 把
tracing.js的初始化逻辑放在require链最顶端,例如在index.js第一行:require('./tracing'); - 避免在
launch.json的env里设NODE_OPTIONS=--require ./tracing.js—— 这会绕过模块解析,导致 resource 属性丢失 - 手动创建 span 时,务必传入
attributes和kind:tracer.startSpan('db.query', { kind: SpanKind.CLIENT, attributes: { 'db.statement': sql } }) - 检查
resourceFromAttributes是否设置了service.name,否则 Jaeger 无法按服务分组
多服务串联时 trace ID 在跨进程请求中丢失
根本原因通常是 HTTP header 传递被截断,尤其在用 child_process.fork 或 exec 启动子服务时,context 不会自动继承。
- 确保所有服务都用了同一套 OpenTelemetry SDK 版本(
@opentelemetry/sdk-node@^0.47.0及以上) - HTTP 客户端发起请求前,手动注入 context:
propagation.inject({ carrier: headers }, context.active()) - 如果用 Express,确认已加
ExpressInstrumentation;如果是 Fastify,需FastifyInstrumentation—— 自动 instrumentations 不跨框架通用 - 在
launch.json的 compound 配置中,给每个服务单独设env,避免 traceparent header 被父进程污染
最常被忽略的一点:OpenTelemetry 的资源(resource)必须在 SDK 启动前就确定,且不能动态修改。一旦 tracerProvider.register() 执行,后续对 resource 的任何变更都不会生效 —— 这会导致所有 span 的 service.name 显示为 unknown,Jaeger 里根本找不到你的服务。











