echo服务span名总为“get /”是因为未使用echootel.middleware,而手动包装handler或在group中调tracer.start()导致无法获取路由路径;正确做法是将echootel.middleware作为首个中间件注册在httperrorhandler之前。

为什么 Echo 服务的 Span 名总是 GET /,而不是真实路由
因为没用 echootel.Middleware,而是手动 wrap handler 或直接在 echo.Group 里调 tracer.Start()。Echo 的路由信息(如 /order/{id})只在框架内部解析后才可用,手动埋点拿不到 c.Path(),最终 span name 回退到 HTTP 方法 + 原始路径。
正确做法是把 echootel.Middleware 放在中间件链最外层,且必须在 echo.HTTPErrorHandler 之前注册——否则错误处理阶段生成的 span 会丢失 route 属性。
- 别写
e.GET("/order/:id", otelHandler(myHandler))这类包装,它绕过了 Echo 路由上下文 - 别在
myHandler内部调tracer.Start(ctx, "order"),这会导致 span name 固定、无法携带语义属性 - 确保
echootel.Middleware是第一个中间件:e.Use(echootel.Middleware(...)),之后再加 JWT、CORS 等
HTTP 客户端调用下游服务时 trace 为什么会断掉
因为用了原生 http.Client,没套 otelhttp.NewClient。原生 client 不会自动注入 traceparent header,下游服务收不到 SpanContext,只能新建 root span。
即使你手动 propagator.Inject(),也容易漏掉 tracestate 或采样决策字段,导致跨服务链路不一致。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
- 必须用
otelhttp.NewClient(http.DefaultClient)替代原生 client - 如果用了自定义
http.Transport,需确保它支持RoundTriphook,否则otelhttp的拦截会失效 - gRPC 场景同理:必须用
otelgrpc.Dial,不能只改ctx传参
异步 goroutine 里的 span 全是 root,trace_id 全为 0
Go 的 context.Context 不跨 goroutine 自动传播,go func() { ... }() 启动的新协程拿到的是 context.Background(),不是带 span 的 request ctx。
这不是 OpenTelemetry 的 bug,是 Go 语言模型决定的——context 必须显式传递。
- 所有异步逻辑必须接收
ctx context.Context参数:go doWork(ctx) - 数据库查询、消息发送、定时任务等后台操作,一律从入参 ctx 派生子 span:
span := tracer.Start(ctx, "db.query") - 切忌在 goroutine 内部重新调
otel.Tracer().Start(context.Background(), ...),这等于主动切断链路
TracerProvider 初始化失败但程序完全不报错
这是最隐蔽的问题:如果 otel.SetTracerProvider(tp) 没执行,或 tp 是 nil,所有 tracer.Start() 都返回空 span,日志里没有 trace_id,HTTP handler 不生成 server span,但代码照常运行、无 panic、无 error log。
根本原因是 OpenTelemetry SDK 设计为“静默降级”,避免可观测性组件拖垮业务主流程。
- 必须在
main()开头一次性初始化:initTracer() → otel.SetTracerProvider(tp) → otel.SetTextMapPropagator(propagation.TraceContext{}) - 千万别在 handler 或 init 函数里重复 new TracerProvider,会导致采样器冲突、exporter 连接泄漏
- 验证是否生效:打印
otel.Tracer("test").Start(context.Background(), "test")返回的 span —— 如果span.SpanContext().TraceID().String()是全 0,说明 provider 没设对










