Skip to content

参考:OTel 集成、span 属性与易错点

基于 Jaeger 1.x + OpenTelemetry · 核于 2026-08

速查

  • Jaeger 定位:CNCF 毕业的分布式追踪系统,OTel trace 主要后端之一。
  • trace/span 模型:trace(一次请求链路,唯一 traceID)= span 树(每个 span 一个处理段,parent/child 组成)。
  • 上下文传播:traceID + spanID 跨服务传递,W3C traceparent 头(标准)/ Jaeger uber-trace-id / Zipkin B3。
  • OTel SDK 三件套:Tracer Provider(创建 tracer)/ Tracer(创建 span)/ Exporter(导出后端)+ Instrumentation(自动埋点)。
  • 采样:头部(入口决定,简单)vs 尾部(结束决定,精准);策略 probabilistic/ratelimiting/remote/const。
  • Jaeger vs Tempo:Jaeger 强属性检索(贵)/ Tempo 按 traceID 查省存储(弱检索,新版 TraceQL 补齐)。
  • Zipkin 并入 OTel:Jaeger 兼容 Zipkin 格式,新项目用 OTel SDK + Jaeger/Tempo。

一、OTel SDK 集成速查

Node.js

js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { OTLPTraceExporter } = require('@opentelemetry/sdk-trace-base');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: 'http://jaeger:4318/v1/traces',      // Jaeger OTLP/HTTP
  }),
  instrumentations: [getNodeAutoInstrumentations()],  // 自动埋点
});
sdk.start();
// 在所有其他 require 之前 startSDK(确保自动埋点 hook 到位)

Python

python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.flask import FlaskInstrumentor

provider = TracerProvider()
provider.add_span_processor(
    BatchSpanProcessor(OTLPSpanExporter(endpoint="http://jaeger:4318/v1/traces"))
)
trace.set_tracer_provider(provider)
FlaskInstrumentor().instrument_app(app)        # 自动埋点 Flask

手动创建 span

js
const { trace } = require('@opentelemetry/api');
const tracer = trace.getTracer('my-app');

async function processOrder(orderId) {
  return tracer.startActiveSpan('processOrder', async (span) => {
    span.setAttribute('order.id', orderId);           // tags
    try {
      const result = await doWork();
      span.setAttribute('result', 'success');
      return result;
    } catch (err) {
      span.recordException(err);                       // 记录异常
      span.setStatus({ code: 2, message: err.message }); // error
      throw err;
    } finally {
      span.end();                                      // 必须 end
    }
  });
}

二、span 属性(tags)速查

标准 OTel 属性(semantic conventions)

属性含义
http.methodGET/POST
http.url / http.target请求 URL/路径
http.status_code200/404/500
http.request_content_length请求体大小
rpc.systemgrpc
rpc.service / rpc.methodgRPC 服务/方法
db.systemmysql/postgresql/redis
db.statementSQL 语句
messaging.systemkafka
messaging.destinationtopic 名称
errortrue(错误标记)
otel.scope.nameinstrumentation 名

自定义属性

js
span.setAttribute('order.id', orderId);
span.setAttribute('user.role', 'vip');
span.setAttribute('payment.amount', 99.9);
// 高基数字段(如 trace_id 本身)慎用——索引成本高

三、采样配置速查

SDK 端头部采样(traceSampler)

js
// OTel SDK 配置(Node)
const { ParentBasedSampler, TraceIdRatioBasedSampler } = require('@opentelemetry/sdk-trace-base');
const sdk = new NodeSDK({
  traceExporter: ...,
  sampler: new ParentBasedSampler({          // 跟随父 span 的采样决定
    rootSampler: new TraceIdRatioBasedSampler(0.01),  // 根采样 1%
  }),
});

Jaeger 远程采样(生产推荐)

yaml
# Jaeger 配置远程采样策略
sampling:
  strategies-file: /etc/jaeger/sampling.json
# sampling.json
{
  "default_strategy": { "type": "probabilistic", "param": 0.001 },
  "service_strategies": [
    {
      "service": "payment-svc",
      "type": "ratelimiting", "param": 100          // 支付 100/s
    },
    {
      "service": "api-gateway",
      "operation_strategies": [
        { "operation": "GET /health", "type": "const", "param": 0 },  // 健康检查不采
        { "operation": "POST /order", "type": "probabilistic", "param": 0.5 }
      ]
    }
  ]
}

OTel Collector 尾部采样

yaml
# otel-collector-config.yaml
processors:
  tail_sampling:
    decision_wait: 10s              # 等请求结束 10s
    num_traces: 50000               # 缓存上限
    policies:
      - { name: errors, type: status_code, status_code: { status_codes: [ERROR] } }  # 错误全采
      - { name: slow, type: latency, latency: { threshold_ms: 1000 } }              # 慢请求全采
      - { name: random, type: probabilistic, probabilistic: { sampling_percentage: 1 } }  # 其他 1%
service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [tail_sampling]
      exporters: [jaeger]

四、上下文传播头速查

W3C Trace Context(标准,OTel 默认)

traceparent: 00-<trace-id>-<span-id>-<flags>
  例:traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
  flags 末位 = sampled(1=采样,0=不采)
tracestate: <vendor-specific>(可选)

Jaeger 格式

uber-trace-id: <trace-id>:<span-id>:<parent-span-id>:<flags>
  例:uber-trace-id: 4bf92f3577b34da6a3ce929d0e0e4736:00f067aa0ba902b7:0000000000000000:01

Zipkin B3 格式

X-B3-TraceId: <trace-id>
X-B3-SpanId: <span-id>
X-B3-ParentSpanId: <parent-span-id>     (根 span 无)
X-B3-Sampled: 0 或 1                     (或 Accept/App)

五、Jaeger 后端部署速查

yaml
# docker-compose 简化(生产用 K8s)
services:
  jaeger:
    image: jaegertracing/all-in-one:1.x   # 测试用 all-in-one
    ports: ["16686:16686", "4318:4318"]   # UI: 16686, OTLP/HTTP: 4318
    environment:
      COLLECTOR_OTLP_ENABLED: "true"      # 启用 OTLP 接收
# 生产用:
#   jaeger-collector(多副本)+ Cassandra/ES 集群 + jaeger-query

六、易错点清单

  • 「Jaeger 替代 ELK 做日志」:错。Jaeger 是追踪(trace/span),日志归 ELK/Loki。
  • 「traceID 全局唯一不必传播」:错。traceID 必须随请求跨服务传播(header/metadata),否则下游不知自己是哪个 trace。
  • 「采样率越高越好」:错。高采样成本爆炸,要按价值采样(尾部采样保证采到错误)。
  • 「span 不 end 也能记录」:错。span 必须 span.end() 才记录 duration 并导出,忘了 end 就丢。
  • 「Jaeger 不需要 SDK 埋点」:错。要 OTel SDK 埋点(自动+手动)创建 span,Jaeger 只存查询。
  • 「Tempo 比 Jaeger 全能」:错。Tempo 弱属性检索(早期只能 traceID),要强属性检索选 Jaeger。
  • 「尾部采样在 SDK 实现」:错。尾部采样在 OTel Collector(要缓存所有 trace 到结束),SDK 做头部采样。
  • 「W3C Trace Context 是 Jaeger 专属」:错。W3C 是业界标准(跨厂商),Jaeger/Tempo/Zipkin 都支持。
  • 「error=true 自动设置」:错。要手动 span.setStatus({code: 2}) 或 recordException,否则 span 不标错误。
  • 「Zipkin 与 Jaeger 不兼容」:错。Jaeger 支持接收 Zipkin 格式数据,可无缝迁移。
  • 「trace 覆盖所有请求」:错。受采样影响,只有被采的请求有 trace(排障可能恰好没采到——尾部采样解决)。
  • 「Jaeger 自带指标监控」:错。Jaeger 是追踪,指标归 Prometheus;Jaeger UI 的 metrics 仅 trace 衍生统计。

权威链接