go.undefinedlabs.com/scopeagent@v0.4.2/instrumentation/grpc/server.go (about)

     1  package grpc
     2  
     3  import (
     4  	"github.com/opentracing/opentracing-go"
     5  	"github.com/opentracing/opentracing-go/ext"
     6  	"github.com/opentracing/opentracing-go/log"
     7  	"go.undefinedlabs.com/scopeagent/instrumentation"
     8  	"golang.org/x/net/context"
     9  	"google.golang.org/grpc"
    10  	"google.golang.org/grpc/metadata"
    11  )
    12  
    13  // OpenTracingServerInterceptor returns a grpc.UnaryServerInterceptor suitable
    14  // for use in a grpc.NewServer call.
    15  //
    16  // For example:
    17  //
    18  //     s := grpc.NewServer(
    19  //         ...,  // (existing ServerOptions)
    20  //         grpc.UnaryInterceptor(otgrpc.OpenTracingServerInterceptor(tracer)))
    21  //
    22  // All gRPC server spans will look for an OpenTracing SpanContext in the gRPC
    23  // metadata; if found, the server span will act as the ChildOf that RPC
    24  // SpanContext.
    25  //
    26  // Root or not, the server Span will be embedded in the context.Context for the
    27  // application-specific gRPC handler(s) to access.
    28  func OpenTracingServerInterceptor(tracer opentracing.Tracer, optFuncs ...Option) grpc.UnaryServerInterceptor {
    29  	otgrpcOpts := newOptions()
    30  	otgrpcOpts.apply(optFuncs...)
    31  	return func(
    32  		ctx context.Context,
    33  		req interface{},
    34  		info *grpc.UnaryServerInfo,
    35  		handler grpc.UnaryHandler,
    36  	) (resp interface{}, err error) {
    37  		if _, ok := tracer.(opentracing.NoopTracer); ok {
    38  			tracer = instrumentation.Tracer()
    39  		}
    40  		spanContext, err := extractSpanContext(ctx, tracer)
    41  		if err != nil && err != opentracing.ErrSpanContextNotFound {
    42  			instrumentation.Logger().Println(err)
    43  		}
    44  		if otgrpcOpts.inclusionFunc != nil &&
    45  			!otgrpcOpts.inclusionFunc(spanContext, info.FullMethod, req, nil) {
    46  			return handler(ctx, req)
    47  		}
    48  		serverSpan := tracer.StartSpan(
    49  			info.FullMethod,
    50  			ext.RPCServerOption(spanContext),
    51  			gRPCComponentTag,
    52  			gRPCPeerServiceTag,
    53  		)
    54  		defer serverSpan.Finish()
    55  		serverSpan.SetTag(MethodName, info.FullMethod)
    56  		serverSpan.SetTag(MethodType, "UNITARY")
    57  
    58  		ctx = opentracing.ContextWithSpan(ctx, serverSpan)
    59  		if otgrpcOpts.logPayloads {
    60  			serverSpan.LogFields(log.Object("gRPC request", req))
    61  		}
    62  		resp, err = handler(ctx, req)
    63  		if err == nil {
    64  			if otgrpcOpts.logPayloads {
    65  				serverSpan.LogFields(log.Object("gRPC response", resp))
    66  			}
    67  			serverSpan.SetTag(Status, "OK")
    68  		} else {
    69  			SetSpanTags(serverSpan, err, false)
    70  			serverSpan.LogFields(log.String("event", "error"), log.String("message", err.Error()))
    71  		}
    72  		if otgrpcOpts.decorator != nil {
    73  			otgrpcOpts.decorator(serverSpan, info.FullMethod, req, resp, err)
    74  		}
    75  		return resp, err
    76  	}
    77  }
    78  
    79  // OpenTracingStreamServerInterceptor returns a grpc.StreamServerInterceptor suitable
    80  // for use in a grpc.NewServer call. The interceptor instruments streaming RPCs by
    81  // creating a single span to correspond to the lifetime of the RPC's stream.
    82  //
    83  // For example:
    84  //
    85  //     s := grpc.NewServer(
    86  //         ...,  // (existing ServerOptions)
    87  //         grpc.StreamInterceptor(otgrpc.OpenTracingStreamServerInterceptor(tracer)))
    88  //
    89  // All gRPC server spans will look for an OpenTracing SpanContext in the gRPC
    90  // metadata; if found, the server span will act as the ChildOf that RPC
    91  // SpanContext.
    92  //
    93  // Root or not, the server Span will be embedded in the context.Context for the
    94  // application-specific gRPC handler(s) to access.
    95  func OpenTracingStreamServerInterceptor(tracer opentracing.Tracer, optFuncs ...Option) grpc.StreamServerInterceptor {
    96  	otgrpcOpts := newOptions()
    97  	otgrpcOpts.apply(optFuncs...)
    98  	return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
    99  		if _, ok := tracer.(opentracing.NoopTracer); ok {
   100  			tracer = instrumentation.Tracer()
   101  		}
   102  		spanContext, err := extractSpanContext(ss.Context(), tracer)
   103  		if err != nil && err != opentracing.ErrSpanContextNotFound {
   104  			instrumentation.Logger().Println(err)
   105  		}
   106  		if otgrpcOpts.inclusionFunc != nil &&
   107  			!otgrpcOpts.inclusionFunc(spanContext, info.FullMethod, nil, nil) {
   108  			return handler(srv, ss)
   109  		}
   110  
   111  		serverSpan := tracer.StartSpan(
   112  			info.FullMethod,
   113  			ext.RPCServerOption(spanContext),
   114  			gRPCComponentTag,
   115  			gRPCPeerServiceTag,
   116  		)
   117  		defer serverSpan.Finish()
   118  		serverSpan.SetTag(MethodName, info.FullMethod)
   119  		if info.IsClientStream {
   120  			serverSpan.SetTag(MethodType, "CLIENT_STREAMING")
   121  		}
   122  		if info.IsServerStream {
   123  			serverSpan.SetTag(MethodType, "SERVER_STREAMING")
   124  		}
   125  
   126  		ss = &openTracingServerStream{
   127  			ServerStream: ss,
   128  			ctx:          opentracing.ContextWithSpan(ss.Context(), serverSpan),
   129  		}
   130  		err = handler(srv, ss)
   131  		if err != nil {
   132  			SetSpanTags(serverSpan, err, false)
   133  			serverSpan.LogFields(log.String("event", "error"), log.String("message", err.Error()))
   134  		}
   135  		if otgrpcOpts.decorator != nil {
   136  			otgrpcOpts.decorator(serverSpan, info.FullMethod, nil, nil, err)
   137  		}
   138  		return err
   139  	}
   140  }
   141  
   142  type openTracingServerStream struct {
   143  	grpc.ServerStream
   144  	ctx context.Context
   145  }
   146  
   147  func (ss *openTracingServerStream) Context() context.Context {
   148  	return ss.ctx
   149  }
   150  
   151  func extractSpanContext(ctx context.Context, tracer opentracing.Tracer) (opentracing.SpanContext, error) {
   152  	md, ok := metadata.FromIncomingContext(ctx)
   153  	if !ok {
   154  		md = metadata.New(nil)
   155  	}
   156  	return tracer.Extract(opentracing.HTTPHeaders, metadataReaderWriter{md})
   157  }
   158  
   159  // Get server interceptors
   160  func GetServerInterceptors() []grpc.ServerOption {
   161  	tracer := instrumentation.Tracer()
   162  	return []grpc.ServerOption{
   163  		grpc.UnaryInterceptor(OpenTracingServerInterceptor(tracer)),
   164  		grpc.StreamInterceptor(OpenTracingStreamServerInterceptor(tracer)),
   165  	}
   166  }
   167  
   168  func NewServer(opts ...grpc.ServerOption) *grpc.Server {
   169  	opts = append(opts, GetServerInterceptors()...)
   170  	return grpc.NewServer(opts...)
   171  }