直接用 req 中间件集成 opentelemetry tracer 比手动埋点更简洁可靠,因其天然支持 context 传递、自动 span 生命周期管理、错误状态标记,且不污染业务逻辑;手动调用 tracer.start()/span.end() 易漏 cleanup、难统一记录请求响应细节、跨 goroutine 或重试时易断链。

直接用 req 的中间件能力加 OpenTelemetry Tracer,比手动在每个 HTTP 调用前后 tracer.Start()/span.End() 简洁、可靠得多——它天然支持 context 传递、自动 span 生命周期管理、错误状态标记,且不污染业务逻辑。
为什么不用标准 net/http + 手动埋点
手动调用 tracer.Start() 和 span.End() 容易漏掉 cleanup(尤其 panic 或 early return 场景),也难统一记录请求体、响应头、状态码等细节;更麻烦的是,跨 goroutine 或重试时 context 丢失会导致 trace 断链。而 req 的 RequestMiddleware 和 ResponseMiddleware 在底层已确保:span 总在 request 发出前创建、response 收到后自动结束,并把 error 映射为 codes.Error。
req.Client 配合 OpenTelemetry Tracer 的最小集成写法
关键不是“怎么初始化 tracer”,而是“怎么把它注入到 req 的生命周期里”。示例中只需两处改动:
- 创建
req.Client实例时,传入req.WithMiddleware()注册中间件 - 中间件内用
trace.SpanFromContext()获取当前 span(若无则新建),再用span.SetAttributes()记录 URL、method、status 等 - 注意:必须用
ctx = trace.ContextWithSpan(ctx, span)把新 span 注入 context,否则下游调用拿不到 trace 上下文
示例片段:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
client := req.C().SetCommonHeader("User-Agent", "my-app/1.0")
client.Use(func(next req.RequestMiddleware) req.RequestMiddleware {
return func(ctx context.Context, rc *req.RequestConfig) error {
span := trace.SpanFromContext(ctx)
if !span.SpanContext().IsValid() {
ctx, span = tracer.Start(ctx, "http.request")
defer span.End()
}
span.SetAttributes(
attribute.String("http.method", rc.Method),
attribute.String("http.url", rc.URL),
)
ctx = trace.ContextWithSpan(ctx, span)
rc.Context = ctx // ← 这步不能少,否则后续中间件和 transport 拿不到
return next(ctx, rc)
}
})
容易踩的坑:Jaeger 上看不到 span 或 trace 断裂
常见原因有三个:
-
LocalAgentHostPort配置错(比如写成"127.0.0.1:6831"但 Jaeger agent 实际监听"localhost:6831",Docker 网络下二者不等价) - 没调用
otel.SetTextMapPropagator(otelpropagation.NewTextMapPropagator()),导致跨服务调用时 trace-id 不透传 - HTTP 请求用了
req.Get()等快捷方法,但这些方法默认不走 client 实例的 middleware 链——必须显式用client.R().Get(...)
特别注意:req 的全局快捷函数(如 req.Get)是独立 client 实例,不会继承你配置好的 tracing middleware。所有需要追踪的请求,必须通过你构造并配置过的 *req.Client 实例发起。
真正麻烦的从来不是“怎么加 trace”,而是“怎么让 trace 不断、不丢、不乱”。context 传递、span 生命周期、propagator 初始化、client 复用——这四点串不起来,再漂亮的仪表盘也只显示孤点。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










