如何在Golang微服务中用Jaeger构建分布式追踪Span树
分布式追踪问题:Jaeger无法生成Parent ID,无法展示Span层级结构
我有一个微服务,用Jaeger+OpenTracing实现分布式追踪,但现在无法生成Parent ID,Jaeger UI只显示默认操作,看不到Span图和层级结构。
Docker-Compose Jaeger配置
jaeger: container_name: jaeger image: jaegertracing/all-in-one:1.39 restart: always ports: - 5775:5775/udp - 6831:6831/udp - 6832:6832/udp - 5778:5778 - 16686:16686 - 14268:14268 - 9411:9411 environment: - COLLECTOR_OTLP_ENABLED=true - OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:16686" - COLLECTOR_ZIPKIN_HTTP_PORT=9411 - JAEGER_SERVICE_NAME=baran-endpoint - JAEGER_AGENT_HOST=jaeger - JAEGER_REPORTER_LOG_SPANS=true - JAEGER_SAMPLER_TYPE=const - JAEGER_SAMPLER_PARAM=1 - JAEGER_SAMPLER_SERVER_URL= networks: - network-jaeger
Go代码实现
var ( TracingAnalysisEndpoint = "http://localhost:26855/SaleService.svc" ) const AgentSwitch = false func NewJaegerTracer(service string) opentracing.Tracer { if AgentSwitch == true { return NewJaegerTracerAgent(service) } return NewJaegerTracerDirect(service) } func NewJaegerTracerDirect(service string) opentracing.Tracer { sender := transport.NewHTTPTransport( TracingAnalysisEndpoint, ) tracer, _ := jaeger.NewTracer(service, jaeger.NewConstSampler(true), jaeger.NewRemoteReporter(sender, jaeger.ReporterOptions.Logger(jaeger.StdLogger)), ) return tracer } func NewJaegerTracerAgent(service string) opentracing.Tracer { sender, _ := jaeger.NewUDPTransport("",0) tracer, _ := jaeger.NewTracer(service, jaeger.NewConstSampler(true), jaeger.NewRemoteReporter(sender, jaeger.ReporterOptions.Logger(jaeger.StdLogger)), ) return tracer } func StartSpanFromRequest(tracer opentracing.Tracer, r *http.Request, funcDesc string) opentracing.Span { spanCtx, _ := Extract(tracer, r) return tracer.StartSpan(funcDesc, ext.RPCServerOption(spanCtx)) } func Inject(span opentracing.Span, request *http.Request) error { return span.Tracer().Inject( span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(request.Header)) } func Extract(tracer opentracing.Tracer, r *http.Request) (opentracing.SpanContext, error) { return tracer.Extract( opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header)) }
当前Span使用方式
span:= config.StartSpanFromRequest(config.Tracer,ctx.Request(), "customer method") defer span.Finish() span.LogKV(fmt.Sprintf("Customer not found: %s", request.Mobile))
问题分析与解决方案
1. 追踪上报地址错误
你的TracingAnalysisEndpoint设置为http://localhost:26855/SaleService.svc,这不是Jaeger的收集地址。Jaeger HTTP收集的默认地址是http://localhost:14268/api/traces(直接上报到Collector),如果用Agent模式,UDP地址默认是localhost:6831。
修改TracingAnalysisEndpoint为正确地址:
TracingAnalysisEndpoint = "http://localhost:14268/api/traces"
如果用Agent模式,修正UDP地址:
sender, _ := jaeger.NewUDPTransport("localhost", 6831)
2. 缺少全局Tracer注册
OpenTracing需要将初始化的Tracer设置为全局实例,否则跨函数/请求的Span无法关联。在初始化Tracer后添加:
opentracing.SetGlobalTracer(tracer)
3. 父子Span关联的正确方式
- 内部函数调用创建子Span:服务内部调用时,基于父Span创建子Span,使用
opentracing.ChildOf选项:
// 在父Span上下文创建子Span childSpan := tracer.StartSpan("sub-operation", opentracing.ChildOf(span.Context())) defer childSpan.Finish()
- 跨服务调用传递Span上下文:调用其他微服务时,必须将当前Span上下文注入到HTTP请求头,对方服务再Extract作为父Span:
// 发起请求前注入Span上下文 req, _ := http.NewRequest("GET", "http://other-service/api", nil) err := Inject(parentSpan, req) if err != nil { // 处理错误 } // 发送请求 client.Do(req)
4. Docker-Compose环境变量修正
OTEL_EXPORTER_OTLP_ENDPOINT设置错误,Jaeger的OTLP HTTP端点默认是http://localhost:4318,不是UI端口16686,可删除该变量或设置正确值。JAEGER_SERVICE_NAME是客户端配置,应在Go代码中设置,容器侧无需该配置。
修正后的Jaeger环境变量:
environment: - COLLECTOR_OTLP_ENABLED=true - COLLECTOR_ZIPKIN_HTTP_PORT=9411 - JAEGER_REPORTER_LOG_SPANS=true
5. 完善错误处理
代码中忽略了大量错误(如tracer, _ := jaeger.NewTracer),初始化失败时无法排查,需添加错误处理:
tracer, err := jaeger.NewTracer(service, jaeger.NewConstSampler(true), jaeger.NewRemoteReporter(sender)) if err != nil { log.Fatalf("Failed to init Jaeger tracer: %v", err) }
验证步骤
- 修正配置和代码后,重启Jaeger和微服务
- 发起请求,访问Jaeger UI(
http://localhost:16686) - 检查Span是否有Parent ID,是否形成层级结构
内容的提问来源于stack exchange,提问作者r saniei
相关产品推荐
相关产品推荐

