Go语言OpenTelemetry实战分布式追踪系统1. OpenTelemetry概述OpenTelemetryOTel是CNCF的可观测性框架提供追踪、指标、日志的统一标准支持多种后端存储。2. 追踪客户端package otel import ( context go.opentelemetry.io/otel go.opentelemetry.io/otel/attribute go.opentelemetry.io/otel/exporters/jaeger go.opentelemetry.io/otel/sdk/trace semconv go.opentelemetry.io/otel/semconv/v1.17.0 ) type Tracer struct { tracer *trace.Tracer provider *trace.TracerProvider } func NewTracer(serviceName, endpoint string) (*Tracer, error) { exporter, err : jaeger.New( jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(endpoint)), ) if err ! nil { return nil, err } provider : trace.NewTracerProvider( trace.WithBatcher(exporter), trace.WithResource( resource.NewWithAttributes( semconv.ServiceName(serviceName), semconv.ServiceVersion(1.0.0), ), ), trace.WithSampler(trace.AlwaysSample()), ) otel.SetTracerProvider(provider) return Tracer{ tracer: provider.Tracer(serviceName), provider: provider, }, nil } func (t *Tracer) StartSpan(ctx context.Context, name string, attrs ...attribute.KeyValue) (context.Context, *trace.Span) { return t.tracer.Start(ctx, name, trace.WithAttributes(attrs...)) } func (t *Tracer) AddSpanEvents(ctx context.Context, events []trace.Event) { span : trace.SpanFromContext(ctx) for _, event : range events { span.AddEvent(event.Name, trace.WithAttributes(event.Attributes...)) } } func (t *Tracer) RecordError(ctx context.Context, err error) { span : trace.SpanFromContext(ctx) span.RecordError(err) } func (t *Tracer) Shutdown(ctx context.Context) error { return t.provider.Shutdown(ctx) }3. gRPC拦截器package otelgrpc type ServerInterceptor struct { tracer *otel.Tracer } func NewServerInterceptor(tracer *otel.Tracer) *ServerInterceptor { return ServerInterceptor{tracer: tracer} } func (i *ServerInterceptor) StreamInterceptor() grpc.ServerStreamInterceptor { return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { ctx, span : i.tracer.StartSpan( ss.Context(), info.FullMethod, otelgrpc.WithMessageEvents(otelgrpc.ReceivedMessageEvents), ) defer span.End() return handler(srv, serverStream{ss, ctx}) } } type serverStream struct { grpc.ServerStream ctx context.Context } func (s *serverStream) Context() context.Context { return s.ctx } type ClientInterceptor struct { tracer *otel.Tracer } func (i *ClientInterceptor) UnaryInterceptor() grpc.UnaryClientInterceptor { return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { ctx, span : i.tracer.StartSpan(ctx, method) defer span.End() return invoker(ctx, method, req, reply, cc, opts...) } }4. HTTP中间件package otelhttp func Middleware(tracer *otel.Tracer) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx, span : tracer.StartSpan(r.Context(), r.URL.Path, attribute.String(http.method, r.Method), attribute.String(http.url, r.URL.String()), ) defer span.End() r r.WithContext(ctx) rw : responseWriter{ResponseWriter: w, statusCode: http.StatusOK} next.ServeHTTP(rw, r) span.SetAttributes( attribute.Int(http.status_code, rw.statusCode), ) }) } } type responseWriter struct { http.ResponseWriter statusCode int } func (rw *responseWriter) WriteHeader(code int) { rw.statusCode code rw.ResponseWriter.WriteHeader(code) }5. Baggage传播func InjectBaggage(ctx context.Context, key, value string) context.Context { return otel.GetTextMapPropagator().Inject(ctx, propagation.MapCarrier{ key: value, }) } func ExtractBaggage(ctx context.Context, key string) string { carrier : propagation.MapCarrier{} ctx otel.GetTextMapPropagator().Extract(ctx, carrier) return carrier[key] }6. 总结OpenTelemetry为Go语言提供了完整的分布式追踪能力通过统一的标准和丰富的SDK可以实现微服务架构下的全链路追踪。