github.com/rudderlabs/rudder-go-kit@v0.30.0/stats/traces.go (about)

     1  package stats
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"go.opentelemetry.io/otel/codes"
     8  	"go.opentelemetry.io/otel/propagation"
     9  	"go.opentelemetry.io/otel/trace"
    10  )
    11  
    12  // SpanStatus is an 32-bit representation of a status state.
    13  type SpanStatus uint32
    14  
    15  const (
    16  	// SpanStatusUnset is the default status code.
    17  	SpanStatusUnset = SpanStatus(codes.Unset)
    18  
    19  	// SpanStatusError indicates the operation contains an error.
    20  	SpanStatusError = SpanStatus(codes.Error)
    21  
    22  	// SpanStatusOk indicates operation has been validated by an Application developers
    23  	// or Operator to have completed successfully, or contain no error.
    24  	SpanStatusOk = SpanStatus(codes.Ok)
    25  )
    26  
    27  type (
    28  	SpanKind    = trace.SpanKind
    29  	SpanContext = trace.SpanContext
    30  )
    31  
    32  const (
    33  	// SpanKindUnspecified is an unspecified SpanKind and is not a valid
    34  	// SpanKind. SpanKindUnspecified should be replaced with SpanKindInternal
    35  	// if it is received.
    36  	SpanKindUnspecified = trace.SpanKindUnspecified
    37  	// SpanKindInternal is a SpanKind for a Span that represents an internal
    38  	// operation within an application.
    39  	SpanKindInternal = trace.SpanKindInternal
    40  	// SpanKindServer is a SpanKind for a Span that represents the operation
    41  	// of handling a request from a client.
    42  	SpanKindServer = trace.SpanKindServer
    43  	// SpanKindClient is a SpanKind for a Span that represents the operation
    44  	// of client making a request to a server.
    45  	SpanKindClient = trace.SpanKindClient
    46  	// SpanKindProducer is a SpanKind for a Span that represents the operation
    47  	// of a producer sending a message to a message broker. Unlike
    48  	// SpanKindClient and SpanKindServer, there is often no direct
    49  	// relationship between this kind of Span and a SpanKindConsumer kind. A
    50  	// SpanKindProducer Span will end once the message is accepted by the
    51  	// message broker which might not overlap with the processing of that
    52  	// message.
    53  	SpanKindProducer = trace.SpanKindProducer
    54  	// SpanKindConsumer is a SpanKind for a Span that represents the operation
    55  	// of a consumer receiving a message from a message broker. Like
    56  	// SpanKindProducer Spans, there is often no direct relationship between
    57  	// this Span and the Span that produced the message.
    58  	SpanKindConsumer = trace.SpanKindConsumer
    59  )
    60  
    61  type Tracer interface {
    62  	SpanFromContext(context.Context) TraceSpan
    63  	Start(
    64  		ctx context.Context, spanName string, spanKind SpanKind, options ...SpanOption,
    65  	) (context.Context, TraceSpan)
    66  }
    67  
    68  type TraceSpan interface {
    69  	// AddEvent adds an event with the provided name and options.
    70  	AddEvent(name string, options ...SpanOption)
    71  
    72  	// SetStatus sets the status of the Span in the form of a code and a
    73  	// description, provided the status hasn't already been set to a higher
    74  	// value before (OK > Error > Unset). The description is only included in a
    75  	// status when the code is for an error.
    76  	SetStatus(code SpanStatus, description string)
    77  
    78  	// SpanContext returns the SpanContext of the Span. The returned SpanContext
    79  	// is usable even after the End method has been called for the Span.
    80  	SpanContext() SpanContext
    81  
    82  	// SetAttributes sets kv as attributes of the Span. If a key from kv
    83  	// already exists for an attribute of the Span it will be overwritten with
    84  	// the value contained in kv.
    85  	SetAttributes(tags Tags)
    86  
    87  	// End terminates the span
    88  	End()
    89  }
    90  
    91  // NewTracerFromOpenTelemetry creates a new go-kit Tracer from an OpenTelemetry Tracer.
    92  func NewTracerFromOpenTelemetry(t trace.Tracer) Tracer {
    93  	return &tracer{tracer: t}
    94  }
    95  
    96  type tracer struct {
    97  	tracer trace.Tracer
    98  }
    99  
   100  func (t *tracer) SpanFromContext(ctx context.Context) TraceSpan {
   101  	return &span{span: trace.SpanFromContext(ctx)}
   102  }
   103  
   104  func (t *tracer) Start(ctx context.Context, spanName string, spanKind SpanKind, opts ...SpanOption) (
   105  	context.Context, TraceSpan,
   106  ) {
   107  	var sc spanConfig
   108  	for _, opt := range opts {
   109  		opt(&sc)
   110  	}
   111  
   112  	var startOpts []trace.SpanStartOption
   113  	if len(sc.tags) > 0 {
   114  		startOpts = append(startOpts, trace.WithAttributes(sc.tags.otelAttributes()...))
   115  	}
   116  	if !sc.timestamp.IsZero() {
   117  		startOpts = append(startOpts, trace.WithTimestamp(sc.timestamp))
   118  	}
   119  	if spanKind != SpanKindUnspecified {
   120  		startOpts = append(startOpts, trace.WithSpanKind(spanKind))
   121  	}
   122  
   123  	ctx, s := t.tracer.Start(ctx, spanName, startOpts...)
   124  	return ctx, &span{span: s}
   125  }
   126  
   127  type spanConfig struct {
   128  	tags      Tags
   129  	timestamp time.Time
   130  }
   131  
   132  // SpanOption can be used with span.Start() and span.AddEvent()
   133  type SpanOption func(*spanConfig)
   134  
   135  // SpanWithTags sets the tags for the span
   136  func SpanWithTags(tags Tags) SpanOption {
   137  	return func(c *spanConfig) {
   138  		c.tags = tags
   139  	}
   140  }
   141  
   142  // SpanWithTimestamp sets the timestamp for the span
   143  func SpanWithTimestamp(timestamp time.Time) SpanOption {
   144  	return func(c *spanConfig) {
   145  		c.timestamp = timestamp
   146  	}
   147  }
   148  
   149  type span struct {
   150  	span trace.Span
   151  }
   152  
   153  func (s *span) AddEvent(name string, opts ...SpanOption) {
   154  	var sc spanConfig
   155  	for _, opt := range opts {
   156  		opt(&sc)
   157  	}
   158  
   159  	var eventOpts []trace.EventOption
   160  	if len(sc.tags) > 0 {
   161  		eventOpts = append(eventOpts, trace.WithAttributes(sc.tags.otelAttributes()...))
   162  	}
   163  	if !sc.timestamp.IsZero() {
   164  		eventOpts = append(eventOpts, trace.WithTimestamp(sc.timestamp))
   165  	}
   166  
   167  	s.span.AddEvent(name, eventOpts...)
   168  }
   169  
   170  func (s *span) SetStatus(code SpanStatus, description string) {
   171  	s.span.SetStatus(codes.Code(code), description)
   172  }
   173  
   174  func (s *span) SpanContext() SpanContext { return s.span.SpanContext() }
   175  func (s *span) SetAttributes(t Tags)     { s.span.SetAttributes(t.otelAttributes()...) }
   176  func (s *span) End()                     { s.span.End() }
   177  
   178  const (
   179  	traceParentHeader = "traceparent"
   180  )
   181  
   182  // GetTraceParentFromContext returns the traceparent header from the context
   183  func GetTraceParentFromContext(ctx context.Context) string {
   184  	mapCarrier := propagation.MapCarrier{}
   185  	(propagation.TraceContext{}).Inject(ctx, mapCarrier)
   186  	return mapCarrier[traceParentHeader]
   187  }
   188  
   189  // InjectTraceParentIntoContext injects the traceparent header into the context
   190  func InjectTraceParentIntoContext(ctx context.Context, traceParent string) context.Context {
   191  	return (propagation.TraceContext{}).Extract(ctx, propagation.MapCarrier{
   192  		traceParentHeader: traceParent,
   193  	})
   194  }