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  }