go-micro.dev/v5@v5.12.0/wrapper/trace/opentelemetry/wrapper.go (about)

     1  package opentelemetry
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"go-micro.dev/v5/client"
     8  	"go-micro.dev/v5/registry"
     9  	"go-micro.dev/v5/server"
    10  	"go.opentelemetry.io/otel/codes"
    11  	"go.opentelemetry.io/otel/trace"
    12  )
    13  
    14  // NewCallWrapper accepts an opentracing Tracer and returns a Call Wrapper.
    15  func NewCallWrapper(opts ...Option) client.CallWrapper {
    16  	options := Options{}
    17  	for _, o := range opts {
    18  		o(&options)
    19  	}
    20  	return func(cf client.CallFunc) client.CallFunc {
    21  		return func(ctx context.Context, node *registry.Node, req client.Request, rsp interface{}, opts client.CallOptions) error {
    22  			if options.CallFilter != nil && options.CallFilter(ctx, req) {
    23  				return cf(ctx, node, req, rsp, opts)
    24  			}
    25  			name := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
    26  			spanOpts := []trace.SpanStartOption{
    27  				trace.WithSpanKind(trace.SpanKindClient),
    28  			}
    29  			ctx, span := StartSpanFromContext(ctx, options.TraceProvider, name, spanOpts...)
    30  			defer span.End()
    31  			if err := cf(ctx, node, req, rsp, opts); err != nil {
    32  				span.SetStatus(codes.Error, err.Error())
    33  				span.RecordError(err)
    34  				return err
    35  			}
    36  			return nil
    37  		}
    38  	}
    39  }
    40  
    41  // NewHandlerWrapper accepts an opentracing Tracer and returns a Handler Wrapper.
    42  func NewHandlerWrapper(opts ...Option) server.HandlerWrapper {
    43  	options := Options{}
    44  	for _, o := range opts {
    45  		o(&options)
    46  	}
    47  	return func(h server.HandlerFunc) server.HandlerFunc {
    48  		return func(ctx context.Context, req server.Request, rsp interface{}) error {
    49  			if options.HandlerFilter != nil && options.HandlerFilter(ctx, req) {
    50  				return h(ctx, req, rsp)
    51  			}
    52  			name := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
    53  			spanOpts := []trace.SpanStartOption{
    54  				trace.WithSpanKind(trace.SpanKindServer),
    55  			}
    56  			ctx, span := StartSpanFromContext(ctx, options.TraceProvider, name, spanOpts...)
    57  			defer span.End()
    58  			if err := h(ctx, req, rsp); err != nil {
    59  				span.SetStatus(codes.Error, err.Error())
    60  				span.RecordError(err)
    61  				return err
    62  			}
    63  			return nil
    64  		}
    65  	}
    66  }
    67  
    68  // NewSubscriberWrapper accepts an opentracing Tracer and returns a Subscriber Wrapper.
    69  func NewSubscriberWrapper(opts ...Option) server.SubscriberWrapper {
    70  	options := Options{}
    71  	for _, o := range opts {
    72  		o(&options)
    73  	}
    74  	return func(next server.SubscriberFunc) server.SubscriberFunc {
    75  		return func(ctx context.Context, msg server.Message) error {
    76  			if options.SubscriberFilter != nil && options.SubscriberFilter(ctx, msg) {
    77  				return next(ctx, msg)
    78  			}
    79  			name := "Sub from " + msg.Topic()
    80  			spanOpts := []trace.SpanStartOption{
    81  				trace.WithSpanKind(trace.SpanKindServer),
    82  			}
    83  			ctx, span := StartSpanFromContext(ctx, options.TraceProvider, name, spanOpts...)
    84  			defer span.End()
    85  			if err := next(ctx, msg); err != nil {
    86  				span.SetStatus(codes.Error, err.Error())
    87  				span.RecordError(err)
    88  				return err
    89  			}
    90  			return nil
    91  		}
    92  	}
    93  }
    94  
    95  // NewClientWrapper returns a client.Wrapper
    96  // that adds monitoring to outgoing requests.
    97  func NewClientWrapper(opts ...Option) client.Wrapper {
    98  	options := Options{}
    99  	for _, o := range opts {
   100  		o(&options)
   101  	}
   102  	return func(c client.Client) client.Client {
   103  		w := &clientWrapper{
   104  			Client:        c,
   105  			tp:            options.TraceProvider,
   106  			callFilter:    options.CallFilter,
   107  			streamFilter:  options.StreamFilter,
   108  			publishFilter: options.PublishFilter,
   109  		}
   110  		return w
   111  	}
   112  }
   113  
   114  type clientWrapper struct {
   115  	client.Client
   116  
   117  	tp            trace.TracerProvider
   118  	callFilter    CallFilter
   119  	streamFilter  StreamFilter
   120  	publishFilter PublishFilter
   121  }
   122  
   123  func (w *clientWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
   124  	if w.callFilter != nil && w.callFilter(ctx, req) {
   125  		return w.Client.Call(ctx, req, rsp, opts...)
   126  	}
   127  	name := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
   128  	spanOpts := []trace.SpanStartOption{
   129  		trace.WithSpanKind(trace.SpanKindClient),
   130  	}
   131  	ctx, span := StartSpanFromContext(ctx, w.tp, name, spanOpts...)
   132  	defer span.End()
   133  	if err := w.Client.Call(ctx, req, rsp, opts...); err != nil {
   134  		span.SetStatus(codes.Error, err.Error())
   135  		span.RecordError(err)
   136  		return err
   137  	}
   138  	return nil
   139  }
   140  
   141  func (w *clientWrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
   142  	if w.streamFilter != nil && w.streamFilter(ctx, req) {
   143  		return w.Client.Stream(ctx, req, opts...)
   144  	}
   145  	name := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
   146  	spanOpts := []trace.SpanStartOption{
   147  		trace.WithSpanKind(trace.SpanKindClient),
   148  	}
   149  	ctx, span := StartSpanFromContext(ctx, w.tp, name, spanOpts...)
   150  	defer span.End()
   151  	stream, err := w.Client.Stream(ctx, req, opts...)
   152  	if err != nil {
   153  		span.SetStatus(codes.Error, err.Error())
   154  		span.RecordError(err)
   155  	}
   156  	return stream, err
   157  }
   158  
   159  func (w *clientWrapper) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
   160  	if w.publishFilter != nil && w.publishFilter(ctx, p) {
   161  		return w.Client.Publish(ctx, p, opts...)
   162  	}
   163  	name := fmt.Sprintf("Pub to %s", p.Topic())
   164  	spanOpts := []trace.SpanStartOption{
   165  		trace.WithSpanKind(trace.SpanKindClient),
   166  	}
   167  	ctx, span := StartSpanFromContext(ctx, w.tp, name, spanOpts...)
   168  	defer span.End()
   169  	if err := w.Client.Publish(ctx, p, opts...); err != nil {
   170  		span.SetStatus(codes.Error, err.Error())
   171  		span.RecordError(err)
   172  		return err
   173  	}
   174  	return nil
   175  }