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 }