Iris框架集成Jaeger需在iris.New()前初始化Tracer并注册全局,中间件中显式提取/创建Span并注入Context,HTTP出向调用须手动注入SpanContext,日志需关联traceID,且务必验证Agent端口连通性。

Iris 框架本身不内置分布式追踪,但能通过 opentracing-go 或 opentelemetry-go 与 Jaeger 无缝对接。关键不是“能不能加”,而是“在哪初始化 Tracer”“怎么把 Span 注入 HTTP Context”“如何透传到下游”。漏掉任意一环,链路就断在 Iris 的第一层中间件里。
初始化 Jaeger Tracer 必须在 Iris 启动前完成
Tracer 是全局单例,必须在 iris.New() 之前初始化,否则中间件或 handler 里调用 opentracing.GlobalTracer() 会返回 nil。常见错误是把它塞进某个 handler 或路由注册之后 —— 那时请求都进来了,Tracer 还没建好。
- 使用
uber/jaeger-client-go/v2/config构建配置,Reporter.LocalAgentHostPort必须匹配你实际运行的 Jaeger Agent 端口(默认6831UDP;若用all-in-one镜像且没改参数,它监听的是6832,配错就静默丢数据) -
Sampler.Type建议开发期设为"const"+Param: 1(全采样),上线后改"probabilistic"+Param: 0.1 - 务必调用
cfg.NewTracer()并用opentracing.SetGlobalTracer()注册,否则所有StartSpanFromContext都无效
Iris 中间件里必须提取/创建 Span 并注入 Context
Iris 的 ctx.Request().Context() 是空 context,不带 span。你不显式绑定,后续任何子 span 都无法继承 trace 上下文 —— 所有下游调用(HTTP、gRPC、DB)都会生成孤立 trace。
- 用
tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(ctx.Request().Header))尝试从请求头提取上游 span;失败则新建 root span - 新建 span 后,必须用
opentracing.ContextWithSpan(ctx.Request().Context(), span)包装,并通过ctx.WithContext()传递给后续 handler - 别在中间件末尾直接
span.Finish():Iris 的生命周期里,handler 可能还没执行完,得靠 defer 或统一出口 finish
HTTP 客户端调用必须手动注入 SpanContext
Iris 不自动处理 outbound 请求的 trace 透传。你用 http.DefaultClient 或自定义 client 发起 HTTP 调用时,uber-trace-id 头不会自动带上 —— 即使你前面已把 span 绑定到 ctx,也得自己 extract + inject。
- 在发起请求前,从当前 ctx 提取 span:
span := opentracing.SpanFromContext(ctx) - 创建 carrier:
carrier := opentracing.HTTPHeadersCarrier{} - 注入:
err := tracer.Inject(span.Context(), opentracing.HTTPHeaders, carrier) - 把 carrier 中的 header 全部复制到
req.Header,再执行client.Do(req) - gRPC/thrift/mq 同理,只是 carrier 类型和注入位置不同(比如 gRPC 要用
grpc_md.AppendToOutgoingContext)
别忽略日志和错误传播的上下文关联
很多团队只埋点 span,却让日志脱离 trace。结果查 Jaeger 看到慢调用,翻日志时找不到对应请求的完整上下文 —— 因为日志没打 traceID。
- 在中间件里拿到 span 后,建议把
span.Context().(jaeger.SpanContext).TraceID().String()注入到 Iris 的ctx.Values(),供后续 handler 和日志中间件读取 - panic 或 error 返回时,用
ext.Error.Set(span, true)+span.SetTag("error.kind", "xxx")显式标记,避免 UI 里无法过滤失败链路 - 如果用了结构化日志(如 zap),确保 logger.With() 里传入 traceID 字段,而不是依赖全局变量
最常被跳过的一步:验证 Agent 端口和 Collector 是否真通。跑通本地 all-in-one 不代表生产环境也行 —— Kubernetes 里 Service DNS、网络策略、UDP 包大小限制都可能让 6831 报文被 silently drop。上线前,先用 nc -u localhost 6831 测试连通性,再看 Jaeger UI 是否有数据进来。











