forked from getsentry/sentry-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
propagator.go
126 lines (107 loc) · 4.23 KB
/
propagator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//go:build go1.18
package sentryotel
import (
"context"
"github.com/cdn77/sentry-go"
"github.com/cdn77/sentry-go/internal/otel/baggage"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
)
type sentryPropagator struct{}
func NewSentryPropagator() propagation.TextMapPropagator {
return &sentryPropagator{}
}
// Inject sets Sentry-related values from the Context into the carrier.
//
// https://opentelemetry.io/docs/reference/specification/context/api-propagators/#inject
func (p sentryPropagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) {
spanContext := trace.SpanContextFromContext(ctx)
var sentrySpan *sentry.Span
if spanContext.IsValid() {
sentrySpan, _ = sentrySpanMap.Get(spanContext.SpanID())
} else {
sentrySpan = nil
}
// Propagate sentry-trace header
if sentrySpan == nil {
// No span => propagate the incoming sentry-trace header, if exists
sentryTraceHeader, _ := ctx.Value(sentryTraceHeaderContextKey{}).(string)
if sentryTraceHeader != "" {
carrier.Set(sentry.SentryTraceHeader, sentryTraceHeader)
}
} else {
// Sentry span exists => generate "sentry-trace" from it
carrier.Set(sentry.SentryTraceHeader, sentrySpan.ToSentryTrace())
}
// Propagate baggage header
sentryBaggageStr := ""
if sentrySpan != nil {
sentryBaggageStr = sentrySpan.GetTransaction().ToBaggage()
}
// FIXME(anton): We're basically reparsing the header again, because in sentry-go
// we currently don't expose a method to get only DSC or its baggage (only a string).
// This is not optimal and we should consider other approaches.
sentryBaggage, _ := baggage.Parse(sentryBaggageStr)
// Merge the baggage values
finalBaggage, baggageOk := ctx.Value(baggageContextKey{}).(baggage.Baggage)
if !baggageOk {
finalBaggage = baggage.Baggage{}
}
for _, member := range sentryBaggage.Members() {
var err error
finalBaggage, err = finalBaggage.SetMember(member)
if err != nil {
continue
}
}
if finalBaggage.Len() > 0 {
carrier.Set(sentry.SentryBaggageHeader, finalBaggage.String())
}
}
// Extract reads cross-cutting concerns from the carrier into a Context.
//
// https://opentelemetry.io/docs/reference/specification/context/api-propagators/#extract
func (p sentryPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context {
sentryTraceHeader := carrier.Get(sentry.SentryTraceHeader)
if sentryTraceHeader != "" {
ctx = context.WithValue(ctx, sentryTraceHeaderContextKey{}, sentryTraceHeader)
if traceParentContext, valid := sentry.ParseTraceParentContext([]byte(sentryTraceHeader)); valid {
// Save traceParentContext because we'll at least need to know the original "sampled"
// value in the span processor.
ctx = context.WithValue(ctx, sentryTraceParentContextKey{}, traceParentContext)
spanContextConfig := trace.SpanContextConfig{
TraceID: trace.TraceID(traceParentContext.TraceID),
SpanID: trace.SpanID(traceParentContext.ParentSpanID),
TraceFlags: trace.FlagsSampled,
Remote: true,
}
ctx = trace.ContextWithSpanContext(ctx, trace.NewSpanContext(spanContextConfig))
}
}
baggageHeader := carrier.Get(sentry.SentryBaggageHeader)
if baggageHeader != "" {
// Preserve the original baggage
parsedBaggage, err := baggage.Parse(baggageHeader)
if err == nil {
ctx = context.WithValue(ctx, baggageContextKey{}, parsedBaggage)
}
}
// The following cases should be already covered below:
// * We can extract a valid dynamic sampling context (DSC) from the baggage
// * No baggage header is present
// * No Sentry-related values are present
// * We cannot parse the baggage header for whatever reason
dynamicSamplingContext, err := sentry.DynamicSamplingContextFromHeader([]byte(baggageHeader))
if err != nil {
// If there are any errors, create a new non-frozen one.
dynamicSamplingContext = sentry.DynamicSamplingContext{Frozen: false}
}
ctx = context.WithValue(ctx, dynamicSamplingContextKey{}, dynamicSamplingContext)
return ctx
}
// Fields returns a list of fields that will be used by the propagator.
//
// https://opentelemetry.io/docs/reference/specification/context/api-propagators/#fields
func (p sentryPropagator) Fields() []string {
return []string{sentry.SentryTraceHeader, sentry.SentryBaggageHeader}
}