github.com/blend/go-sdk@v1.20220411.3/tracing/grpctrace/tracer.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package grpctrace 9 10 import ( 11 "context" 12 "time" 13 14 opentracing "github.com/opentracing/opentracing-go" 15 16 "google.golang.org/grpc/metadata" 17 18 "github.com/blend/go-sdk/grpcutil" 19 "github.com/blend/go-sdk/tracing" 20 ) 21 22 var ( 23 _ grpcutil.ClientTracer = (*tracer)(nil) 24 _ grpcutil.ServerTracer = (*tracer)(nil) 25 ) 26 27 // Tracer returns a tracer. 28 func Tracer(t opentracing.Tracer) grpcutil.Tracer { 29 return tracer{ 30 Tracer: t, 31 } 32 } 33 34 // Tracer implements grpcutil.ClientTracer and grpcutil.ServerTracer. 35 type tracer struct { 36 opentracing.Tracer 37 } 38 39 // StartClientUnary starts a unary client trace. 40 func (t tracer) StartClientUnary(ctx context.Context, remoteAddr, method string) (context.Context, grpcutil.TraceFinisher, error) { 41 finisher := TraceFinisher{ 42 startTime: time.Now().UTC(), 43 } 44 45 startOptions := []opentracing.StartSpanOption{ 46 opentracing.Tag{Key: tracing.TagKeySpanType, Value: tracing.SpanTypeRPC}, 47 opentracing.Tag{Key: tracing.TagKeyResourceName, Value: method}, 48 opentracing.Tag{Key: tracing.TagKeyGRPCRemoteAddr, Value: remoteAddr}, 49 opentracing.Tag{Key: tracing.TagKeyGRPCMethod, Value: method}, 50 opentracing.Tag{Key: tracing.TagKeyGRPCRole, Value: "client"}, 51 opentracing.Tag{Key: tracing.TagKeyGRPCCallingConvention, Value: "unary"}, 52 tracing.TagMeasured(), 53 opentracing.StartTime(finisher.startTime), 54 } 55 56 finisher.span, finisher.ctx = tracing.StartSpanFromContext(ctx, t.Tracer, tracing.OperationGRPCClientUnary, startOptions...) 57 md := make(metadata.MD) 58 if err := t.Tracer.Inject(finisher.span.Context(), opentracing.TextMap, MetadataReaderWriter{md}); err != nil { 59 return nil, nil, err 60 } 61 finisher.ctx = metadata.NewOutgoingContext(finisher.ctx, md) 62 return finisher.ctx, finisher, nil 63 } 64 65 // StartClientStream starts a stream client trace. 66 func (t tracer) StartClientStream(ctx context.Context, remoteAddr, method string) (context.Context, grpcutil.TraceFinisher, error) { 67 var finisher TraceFinisher 68 finisher.ctx = ctx 69 finisher.startTime = time.Now().UTC() 70 71 startOptions := []opentracing.StartSpanOption{ 72 opentracing.Tag{Key: tracing.TagKeySpanType, Value: tracing.SpanTypeRPC}, 73 opentracing.Tag{Key: tracing.TagKeyGRPCRemoteAddr, Value: remoteAddr}, 74 opentracing.Tag{Key: tracing.TagKeyResourceName, Value: method}, 75 opentracing.Tag{Key: tracing.TagKeyGRPCMethod, Value: method}, 76 opentracing.Tag{Key: tracing.TagKeyGRPCRole, Value: "client"}, 77 opentracing.Tag{Key: tracing.TagKeyGRPCCallingConvention, Value: "stream"}, 78 opentracing.StartTime(finisher.startTime), 79 } 80 81 finisher.span, finisher.ctx = tracing.StartSpanFromContext(finisher.ctx, t.Tracer, tracing.OperationGRPCClientStream, startOptions...) 82 md := make(metadata.MD) 83 if err := t.Tracer.Inject(finisher.span.Context(), opentracing.TextMap, MetadataReaderWriter{md}); err != nil { 84 return nil, nil, err 85 } 86 finisher.ctx = metadata.NewOutgoingContext(finisher.ctx, md) 87 return finisher.ctx, finisher, nil 88 } 89 90 // StartServerUnary starts a unary server trace. 91 func (t tracer) StartServerUnary(ctx context.Context, method string) (context.Context, grpcutil.TraceFinisher, error) { 92 var finisher TraceFinisher 93 finisher.startTime = time.Now().UTC() 94 finisher.ctx = ctx 95 96 md, ok := metadata.FromIncomingContext(ctx) 97 if !ok { 98 md = metadata.New(nil) 99 } 100 101 authority := grpcutil.MetaValue(md, grpcutil.MetaTagAuthority) 102 contentType := grpcutil.MetaValue(md, grpcutil.MetaTagContentType) 103 userAgent := grpcutil.MetaValue(md, grpcutil.MetaTagUserAgent) 104 105 startOptions := []opentracing.StartSpanOption{ 106 opentracing.Tag{Key: tracing.TagKeySpanType, Value: tracing.SpanTypeRPC}, 107 opentracing.Tag{Key: tracing.TagKeyResourceName, Value: method}, 108 opentracing.Tag{Key: tracing.TagKeyGRPCMethod, Value: method}, 109 opentracing.Tag{Key: tracing.TagKeyGRPCAuthority, Value: authority}, 110 opentracing.Tag{Key: tracing.TagKeyGRPCUserAgent, Value: userAgent}, 111 opentracing.Tag{Key: tracing.TagKeyGRPCContentType, Value: contentType}, 112 opentracing.Tag{Key: tracing.TagKeyGRPCRole, Value: "server"}, 113 opentracing.Tag{Key: tracing.TagKeyGRPCCallingConvention, Value: "unary"}, 114 opentracing.StartTime(finisher.startTime), 115 } 116 117 // try to extract an incoming span context 118 // this is typically done if we're a service being called in a chain from another (more ancestral) 119 // span context. 120 spanContext, _ := t.Tracer.Extract(opentracing.HTTPHeaders, MetadataReaderWriter{md}) 121 if spanContext != nil { 122 startOptions = append(startOptions, opentracing.ChildOf(spanContext)) 123 } 124 125 finisher.span = t.Tracer.StartSpan(tracing.OperationGRPCServerUnary, startOptions...) 126 finisher.ctx = opentracing.ContextWithSpan(finisher.ctx, finisher.span) 127 return finisher.ctx, finisher, nil 128 } 129 130 // StartServerStream starts a stream server trace. 131 func (t tracer) StartServerStream(ctx context.Context, method string) (context.Context, grpcutil.TraceFinisher, error) { 132 var finisher TraceFinisher 133 finisher.startTime = time.Now().UTC() 134 finisher.ctx = ctx 135 136 md, ok := metadata.FromIncomingContext(ctx) 137 if !ok { 138 md = metadata.New(nil) 139 } 140 141 authority := grpcutil.MetaValue(md, grpcutil.MetaTagAuthority) 142 contentType := grpcutil.MetaValue(md, grpcutil.MetaTagContentType) 143 userAgent := grpcutil.MetaValue(md, grpcutil.MetaTagUserAgent) 144 startOptions := []opentracing.StartSpanOption{ 145 opentracing.Tag{Key: tracing.TagKeySpanType, Value: tracing.SpanTypeRPC}, 146 opentracing.Tag{Key: tracing.TagKeyResourceName, Value: method}, 147 opentracing.Tag{Key: tracing.TagKeyGRPCMethod, Value: method}, 148 opentracing.Tag{Key: tracing.TagKeyGRPCAuthority, Value: authority}, 149 opentracing.Tag{Key: tracing.TagKeyGRPCUserAgent, Value: userAgent}, 150 opentracing.Tag{Key: tracing.TagKeyGRPCContentType, Value: contentType}, 151 opentracing.Tag{Key: tracing.TagKeyGRPCRole, Value: "server"}, 152 opentracing.Tag{Key: tracing.TagKeyGRPCCallingConvention, Value: "stream"}, 153 opentracing.StartTime(finisher.startTime), 154 } 155 156 // try to extract an incoming span context 157 // this is typically done if we're a service being called in a chain from another (more ancestral) 158 // span context. 159 spanContext, _ := t.Tracer.Extract(opentracing.HTTPHeaders, MetadataReaderWriter{md}) 160 if spanContext != nil { 161 startOptions = append(startOptions, opentracing.ChildOf(spanContext)) 162 } 163 finisher.span = t.Tracer.StartSpan(tracing.OperationGRPCServerStream, startOptions...) 164 finisher.ctx = opentracing.ContextWithSpan(ctx, finisher.span) 165 return ctx, finisher, nil 166 } 167 168 // TraceFinisher finishes traces. 169 type TraceFinisher struct { 170 startTime time.Time 171 ctx context.Context 172 span opentracing.Span 173 } 174 175 // Finish implements TraceFinisher. 176 func (tf TraceFinisher) Finish(err error) { 177 if err != nil { 178 tracing.SpanError(tf.span, err) 179 } 180 tf.span.Finish() 181 }