jaeger tracer 初始化必须在 main() 开头完成,不能懒加载;需构造 config.configuration、确保 servicename 非空、配置 sampler,调用 cfg.newtracer() 后立即设全局 tracer 并校验非空,避免 panic 和链路断裂。

Jaeger tracer 初始化必须在 main 开头完成,不能懒加载
Go 的 opentracing.GlobalTracer() 默认返回空实现,opentracing.StartSpan() 一调就 panic,错误堆栈里还看不到明确提示。Echo 启动快、中间件注册早,如果 tracer 在某个 handler 或 middleware 初始化,大概率还没来得及设就被用了。
必须在 main() 函数最开头、echo.New() 之前完成三件事:
- 构造
config.Configuration,确保ServiceName非空(填空字符串也会导致 UI 显示unknown_service) -
Sampler不能为 nil:开发用jaeger.NewConstSampler(true),生产建议jaeger.NewProbabilisticSampler(0.1) - 调用
cfg.NewTracer()后立即执行opentracing.SetGlobalTracer(tracer),并检查tracer == nil || err != nil
别在 init() 里做——环境变量可能没加载;也别 defer closer.Close() 到 echo shutdown 钩子之外,否则 goroutine 泄漏。
Echo 中间件必须用 othttp.Middleware 或手动注入/提取 context
标准 http.Handler 不自动处理 trace 上下文,Echo 的 echo.MiddlewareFunc 也不是魔法。直接写 span := opentracing.StartSpan("http") 只会创建孤立 root span,上游 traceID 彻底丢失。
推荐做法是用官方 othttp.Middleware:
import "github.com/opentracing-contrib/go-stdlib/nethttp"
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return othttp.Middleware(othttp.WithTracer(opentracing.GlobalTracer()))(next)
})
如果不用 othttp,手写中间件时必须:
- 用
tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(c.Request().Header))提取上下文,失败才新建 root span - 调用
opentracing.ContextWithSpan(c.Request().Context(), span)绑定到 request context - 再用
c.SetRequest(c.Request().WithContext(...))把新 context 注入 Echo context
漏掉最后一步,后续所有 c.Get("span") 或自定义中间件都拿不到 parent span。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
UDP reporter 在 Docker 环境连不上 agent 是静默丢数据,不是报错
本地开发配 LocalAgentHostPort: "localhost:6831" 能跑,但放进 Docker Compose 就断链——因为容器内 localhost 指向自己,不是 host 上的 Jaeger Agent。
常见修复方式:
- Docker Compose:把地址改成
"jaeger-agent:6831"(服务名需与docker-compose.yml中 service 名一致) - Docker Desktop(Mac/Windows):用
"host.docker.internal:6831" - Kubernetes:用
"jaeger-agent.default.svc.cluster.local:6831"
UDP 协议不建连、不重试、不报错。填错地址,span 就彻底消失,日志里连 warning 都没有。调试阶段建议临时换 HTTP reporter:jaeger.NewHTTPReporter("http://jaeger-collector:14268/api/traces"),失败会明确 panic 并打印连接错误。
Tag 和 Log 值传指针会导致显示 <nil></nil> 或崩溃
Jaeger Go SDK 对类型极其敏感:span.SetTag("user_id", &u.ID) 传的是指针,UI 里显示 <nil></nil> 或内存地址;span.LogFields(log.Object("err", err)) 在 err == nil 时直接 panic。
安全写法:
- Tag 值必须是基础类型或实现
String()的 struct:span.SetTag("user_id", u.ID)、span.SetTag("status", "success") - Log 字段避免
log.Object:span.LogFields(log.String("error", err.Error()))更稳,err为 nil 时返回空字符串 - Tag 键名别用空格或大写:
http_status_code可以,HTTP-Status-Code在 UI 里可能解析失败
跨 goroutine finish span 是另一个隐形坑——比如在 handler 里起 goroutine 异步发 MQ,却在主 goroutine defer span.Finish(),结果 span 提前关闭,MQ 调用变成无 parent 的孤链。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










