需通过中间件手动解析请求头(优先traceparent,次选X-Trace-ID/X-Span-ID)并存入ctx.Values(),下游调用须显式注入traceparent头,日志需动态绑定trace_id,OTel集成须严格对齐请求生命周期管理Span。

怎么给 Iris 请求注入 trace_id 和 span_id
Iris 本身不内置分布式追踪支持,必须靠中间件手动注入和传播上下文。关键不是“加个包就行”,而是确保每次请求进来时,能从 traceparent 或自定义 header(如 X-Trace-ID)中解析出 trace_id 和 span_id,并存入 ctx.Values() 供后续使用。
常见错误是只生成新 ID、不尝试从上游读取——这会导致链路在网关后就断掉。
- 优先检查请求头
traceparent(W3C 标准),用正则提取trace_id和span_id - 若无
traceparent,再 fallback 到X-Trace-ID+X-Span-ID组合(需与上游服务约定) - 始终用
ctx.Set("trace_id", tid)和ctx.Set("span_id", sid)挂载,避免依赖全局变量或闭包捕获 - 对下游 HTTP 调用,必须手动把当前上下文写入
req.Header.Set("traceparent", ...),Iris 不自动透传
如何让 Iris 的 HTTP 客户端调用也带上追踪头
Iris 的 http.Client 是标准库对象,不感知上下文。如果你用 iris.Context.DoHTTP 或自己封装的 HTTP 请求,必须显式注入追踪头,否则出向调用链路就终止了。
最容易被忽略的是:异步 goroutine 中发起的 HTTP 请求,ctx 已不可用,必须提前把 trace_id 和 span_id 作为参数传进去,或用 context.WithValue 包一层。
- 不要直接复用全局
http.DefaultClient,它没有上下文能力 - 推荐封装一个带上下文的请求函数:
DoTracedRequest(ctx iris.Context, method, url string, body io.Reader) - 在函数内调用
ctx.Get("trace_id")和ctx.Get("span_id"),拼出traceparent值 - 对 gRPC 调用,需用
grpc.WithBlock()+metadata.MD注入trace_id,不能只靠 HTTP 头
为什么日志里 trace_id 总是空或重复
根本原因通常是上下文没正确绑定到日志输出点。Iris 没有像 FastAPI 那样的 request.state 机制,ctx.Values() 只在当前请求生命周期有效;一旦进 goroutine、进中间件外的函数、或调用第三方 logger(如 log.Printf),ID 就丢了。
结构化日志库(如 zerolog 或 zap)必须配合 ctx.Values() 动态注入字段,不能靠启动时一次设置。
- 避免在中间件外直接调用
log.Println,它完全脱离请求上下文 - 用
ctx.Logger().Info().Str("trace_id", tid).Msg(...)这类链式写法(需搭配支持 context 的 logger) - 如果用
zerolog,务必在每条日志前调用logger.With().Str("trace_id", tid).Logger()获取子 logger - 虚拟线程(Go 1.20+ 的
go thread.Start)场景下,必须手动传递trace_id,context.Context不会自动继承
和 OpenTelemetry SDK 集成要注意什么
Iris 不是 OpenTelemetry 官方支持的框架,所以不能直接用 otelhttp.NewHandler 包装路由。你得自己桥接:用 OTel 的 Tracer 创建 Span,再把 span context 写回 traceparent 头。
最易踩的坑是 Span 生命周期管理错位——比如在中间件里 StartSpan,却在 handler 返回后才 End(),导致并发请求的 span 混乱。
- Span 必须和 Iris 请求生命周期严格对齐:在中间件
Before阶段StartSpan,在After阶段End() - 用
otel.GetTextMapPropagator().Inject把 span context 注入 outbound request header - 别用
otel.Tracer("").Start(),必须传入context.WithValue(ctx.Request().Context(), ...)确保父子关系 - 采样率配置要统一,否则 Zipkin/Jaeger 后端看到的 trace 是碎片化的
链路追踪真正难的不是加中间件,而是保证每个 goroutine、每次 outbound 调用、每条日志输出,都带着同一组 trace_id 和 span_id。Iris 的轻量设计意味着这些事都得你亲手串起来,漏掉任意一环,整条链就断在那个点上。


















