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 }