package tracing import ( "context" "github.com/go-kratos/kratos/v2/middleware" "github.com/go-kratos/kratos/v2/transport/grpc" "github.com/go-kratos/kratos/v2/transport/http" "github.com/opentracing/opentracing-go" "github.com/opentracing/opentracing-go/ext" "github.com/opentracing/opentracing-go/log" "google.golang.org/grpc/metadata" ) // Option is tracing option. type Option func(*options) type options struct { tracer opentracing.Tracer } // WithTracer sets a custom tracer to be used for this middleware, otherwise the opentracing.GlobalTracer is used. func WithTracer(tracer opentracing.Tracer) Option { return func(o *options) { o.tracer = tracer } } // Server returns a new server middleware for OpenTracing. func Server(opts ...Option) middleware.Middleware { options := options{ tracer: opentracing.GlobalTracer(), } for _, o := range opts { o(&options) } return func(handler middleware.Handler) middleware.Handler { return func(ctx context.Context, req interface{}) (reply interface{}, err error) { var ( component string operation string spanContext opentracing.SpanContext ) if info, ok := http.FromServerContext(ctx); ok { // HTTP span component = "HTTP" operation = info.Request.RequestURI spanContext, _ = options.tracer.Extract( opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(info.Request.Header), ) } else if info, ok := grpc.FromServerContext(ctx); ok { // gRPC span component = "gRPC" operation = info.FullMethod if md, ok := metadata.FromIncomingContext(ctx); ok { spanContext, _ = options.tracer.Extract( opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(md), ) } } span := options.tracer.StartSpan( operation, ext.RPCServerOption(spanContext), opentracing.Tag{Key: string(ext.Component), Value: component}, ) defer span.Finish() if reply, err = handler(ctx, req); err != nil { ext.Error.Set(span, true) span.LogFields( log.String("event", "error"), log.String("message", err.Error()), ) } return } } } // Client returns a new client middleware for OpenTracing. func Client(opts ...Option) middleware.Middleware { options := options{} for _, o := range opts { o(&options) } return func(handler middleware.Handler) middleware.Handler { return func(ctx context.Context, req interface{}) (reply interface{}, err error) { var ( operation string parent opentracing.SpanContext carrier opentracing.HTTPHeadersCarrier ) if span := opentracing.SpanFromContext(ctx); span != nil { parent = span.Context() } if info, ok := http.FromClientContext(ctx); ok { // HTTP span operation = info.Request.RequestURI carrier = opentracing.HTTPHeadersCarrier(info.Request.Header) } else if info, ok := grpc.FromClientContext(ctx); ok { // gRPC span operation = info.FullMethod if md, ok := metadata.FromOutgoingContext(ctx); ok { carrier = opentracing.HTTPHeadersCarrier(md) ctx = metadata.NewOutgoingContext(ctx, md) } else { md = metadata.Pairs() carrier = opentracing.HTTPHeadersCarrier(md) ctx = metadata.NewOutgoingContext(ctx, md) } } span := options.tracer.StartSpan( operation, opentracing.ChildOf(parent), ext.SpanKindRPCClient, ) defer span.Finish() options.tracer.Inject(span.Context(), opentracing.HTTPHeaders, carrier) ctx = opentracing.ContextWithSpan(ctx, span) // send handler if reply, err = handler(ctx, req); err != nil { ext.Error.Set(span, true) span.LogFields( log.String("event", "error"), log.String("message", err.Error()), ) } return } } }