参考:OTel 集成、span 属性与易错点
基于 Jaeger 1.x + OpenTelemetry · 核于 2026-08
速查
- Jaeger 定位:CNCF 毕业的分布式追踪系统,OTel trace 主要后端之一。
- trace/span 模型:trace(一次请求链路,唯一 traceID)= span 树(每个 span 一个处理段,parent/child 组成)。
- 上下文传播:traceID + spanID 跨服务传递,W3C
traceparent头(标准)/ Jaegeruber-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.method | GET/POST |
http.url / http.target | 请求 URL/路径 |
http.status_code | 200/404/500 |
http.request_content_length | 请求体大小 |
rpc.system | grpc |
rpc.service / rpc.method | gRPC 服务/方法 |
db.system | mysql/postgresql/redis |
db.statement | SQL 语句 |
messaging.system | kafka |
messaging.destination | topic 名称 |
error | true(错误标记) |
otel.scope.name | instrumentation 名 |
自定义属性
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:01Zipkin 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 衍生统计。