Jaeger客户端初始化报nil pointer dereference因未正确初始化tracer,需显式创建配置、校验err、调用InitGlobalTracer并确认GlobalTracer非nil;HTTP埋点需正确传递context、基于request context创建span、及时Finish;UDP上报需确保agent端口匹配或改用HTTP reporter;Span标签应精简必要字段,避免冗余和敏感信息。

Jaeger 客户端初始化为什么总报 nil pointer dereference
常见于没传入有效的 tracer 就直接调用 StartSpan,尤其是用 jaeger.NewTracer 时漏掉 cfg.InitGlobalTracer() 或忘了检查返回的 err。Go 的 tracer 是全局单例,但初始化失败后 opentracing.GlobalTracer() 仍返回默认空实现,后续调用 StartSpan 就 panic。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 始终用
cfg, err := jaeger.NewConfig(...)显式创建配置,别依赖零值 - 初始化后立刻校验:
if err != nil { log.Fatal(err) } - 确保
cfg.InitGlobalTracer()成功返回非 niltracer,再开始业务逻辑 - 本地调试时加一行
log.Printf("tracer: %v", opentracing.GlobalTracer())确认不为<nil>
HTTP 请求埋点时 span 生命周期错乱怎么办
典型现象:请求还没结束,span 就 close 了;或多个中间件嵌套导致 parent span 被提前 finish。根本原因是没正确传递 context.Context,或在 goroutine 中用了过期的 context。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用
opentracing.HTTPHeadersCarrier从 request header 解析uber-trace-id,再调用tracer.Extract(...)拿到 parent span context - 所有子 span 必须基于当前 request context 创建:
span := tracer.StartSpan("http.handle", ext.RPCServerOption(ctx)) - 务必在 handler 返回前调用
span.Finish(),不要 defer 到 goroutine 里 —— HTTP handler 的生命周期和 goroutine 不同步 - 避免在
http.HandlerFunc外部新建 span,比如在 middleware 里 start 了,却在 handler 里 finish,容易漏掉 error 分支
jaeger-client-go 和 jaeger-client-cpp 兼容性要注意什么
Go 客户端默认用 UDP 发送 spans 到 agent,而 agent 默认监听 localhost:6831(compact thrift);如果 agent 配置成只开 14268(HTTP endpoint),Go 客户端就会静默丢数据,无报错、无重试、无日志。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 启动 Jaeger agent 时显式指定
--collector.host-port=127.0.0.1:14268并确认端口监听正常 - Go 端改用 HTTP reporter:
jaeger.NewHTTPReporter("http://localhost:14268/api/traces"),绕过 UDP 不可靠问题 - 若坚持用 UDP,检查防火墙、Docker 网络模式(host 模式才能直连
localhost) - 生产环境禁用
localAgentHostPort,改用 reporter + collector endpoint,避免 agent 单点失效影响上报
Span 标签写太多反而掩盖瓶颈
有人把每个 SQL 参数、HTTP body、用户 ID 全打成 span.SetTag,结果 UI 里 span 展开全是冗余字段,真实耗时高的子调用被淹没。Jaeger 对单个 span 的 tag 数量和 value 长度有限制(默认 256 字段,value 最大 256KB),超限会截断或丢弃。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 只打诊断必需的 tag:
db.statement(截断到 200 字符)、http.status_code、rpc.system - 敏感信息如 token、password 绝对不进 tag,用
span.LogFields()记录 debug 日志(可关) - 高频调用(如 Redis get)禁用
db.statement,改用db.operation: "GET"+db.key: "user:123" - 用
ext.SpanKindRPCClient和ext.PeerService替代手写 service 名,保证上下游链路能自动串联
真正卡住的地方往往藏在 span duration 的分布毛刺里,不是标签越多越清楚。先看 timeline 上哪个 span 持续时间长、方差大,再针对性加 log 或 profile,别一上来就堆 metadata。











