-
Notifications
You must be signed in to change notification settings - Fork 216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(otel): Fix Dynamic Sampling context propagation for nested spans #559
Changes from 3 commits
c29a05f
6a0adca
31ccc17
8cfeb42
c74fe8d
356712c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,37 +28,33 @@ type transactionTestContext struct { | |
sampled sentry.Sampled | ||
} | ||
|
||
type otelTestContext struct { | ||
traceId string | ||
spanId string | ||
traceFlags trace.TraceFlags | ||
} | ||
|
||
func createTransactionAndMaybeSpan(transactionContext transactionTestContext, withSpan bool) { | ||
client, _ := sentry.NewClient(sentry.ClientOptions{ | ||
Dsn: "https://abc@example.com/123", | ||
Environment: "testing", | ||
Release: "1.2.3", | ||
EnableTracing: true, | ||
}) | ||
hub := sentry.NewHub(client, sentry.NewScope()) | ||
ctx := sentry.SetHubOnContext(context.Background(), hub) | ||
|
||
func createTransactionAndMaybeSpan(transactionContext transactionTestContext, withSpan bool) trace.SpanContextConfig { | ||
transaction := sentry.StartTransaction( | ||
ctx, | ||
emptyContextWithSentry(), | ||
transactionContext.name, | ||
sentry.SpanSampled(transactionContext.sampled), | ||
) | ||
|
||
transaction.TraceID = TraceIDFromHex(transactionContext.traceID) | ||
transaction.SpanID = SpanIDFromHex(transactionContext.spanID) | ||
transaction.SetDynamicSamplingContext(sentry.DynamicSamplingContextFromTransaction(transaction)) | ||
|
||
sentrySpanMap.Set(trace.SpanID(transaction.SpanID), transaction) | ||
if withSpan { | ||
span := transaction.StartChild("op") | ||
// We want the child to have the SpanID from transactionContext, so | ||
// we "swap" span IDs from the transaction and the child span. | ||
transaction.SpanID = span.SpanID | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure I fully understand this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When |
||
span.SpanID = SpanIDFromHex(transactionContext.spanID) | ||
sentrySpanMap.Set(trace.SpanID(span.SpanID), span) | ||
} | ||
|
||
transaction.SetDynamicSamplingContext(sentry.DynamicSamplingContextFromTransaction(transaction)) | ||
sentrySpanMap.Set(trace.SpanID(transaction.SpanID), transaction) | ||
|
||
otelContext := trace.SpanContextConfig{ | ||
TraceID: otelTraceIDFromHex(transactionContext.traceID), | ||
SpanID: otelSpanIDFromHex(transactionContext.spanID), | ||
TraceFlags: trace.FlagsSampled, | ||
} | ||
return otelContext | ||
} | ||
|
||
func TestNewSentryPropagator(t *testing.T) { | ||
|
@@ -122,107 +118,86 @@ func TestInjectUsesBaggageOnEmptySpan(t *testing.T) { | |
) | ||
} | ||
|
||
func TestInjectUsesSetsValidTraceFromTransaction(t *testing.T) { | ||
func testInjectUsesSetsValidTrace(t *testing.T, withChildSpan bool) { | ||
tests := []struct { | ||
name string | ||
otelSpanContext otelTestContext | ||
sentryTransactionContext transactionTestContext | ||
baggage *string | ||
sentryTrace *string | ||
wantBaggage *string | ||
wantSentryTrace *string | ||
}{ | ||
{ | ||
name: "should set baggage and sentry-trace when sampled", | ||
otelSpanContext: otelTestContext{ | ||
traceId: "d4cda95b652f4a1592b449d5929fda1b", | ||
spanId: "6e0c63257de34c92", | ||
traceFlags: trace.FlagsSampled, | ||
}, | ||
sentryTransactionContext: transactionTestContext{ | ||
name: "sampled-transaction", | ||
traceID: "d4cda95b652f4a1592b449d5929fda1b", | ||
spanID: "6e0c63257de34c92", | ||
sampled: sentry.SampledTrue, | ||
}, | ||
baggage: stringPtr("sentry-environment=testing,sentry-release=1.2.3,sentry-transaction=sampled-transaction,sentry-public_key=abc,sentry-trace_id=d4cda95b652f4a1592b449d5929fda1b,sentry-sample_rate=1"), | ||
sentryTrace: stringPtr("d4cda95b652f4a1592b449d5929fda1b-6e0c63257de34c92-1"), | ||
wantBaggage: stringPtr("sentry-environment=testing,sentry-release=1.2.3,sentry-transaction=sampled-transaction,sentry-public_key=abc,sentry-trace_id=d4cda95b652f4a1592b449d5929fda1b,sentry-sample_rate=1"), | ||
wantSentryTrace: stringPtr("d4cda95b652f4a1592b449d5929fda1b-6e0c63257de34c92-1"), | ||
}, | ||
{ | ||
name: "should set proper baggage and sentry-trace when not sampled", | ||
otelSpanContext: otelTestContext{ | ||
traceId: "d4cda95b652f4a1592b449d5929fda1b", | ||
spanId: "6e0c63257de34c92", | ||
traceFlags: trace.FlagsSampled, | ||
}, | ||
sentryTransactionContext: transactionTestContext{ | ||
name: "not-sampled-transaction", | ||
traceID: "d4cda95b652f4a1592b449d5929fda1b", | ||
spanID: "6e0c63257de34c92", | ||
sampled: sentry.SampledFalse, | ||
}, | ||
baggage: stringPtr("sentry-environment=testing,sentry-release=1.2.3,sentry-transaction=not-sampled-transaction,sentry-public_key=abc,sentry-trace_id=d4cda95b652f4a1592b449d5929fda1b"), | ||
sentryTrace: stringPtr("d4cda95b652f4a1592b449d5929fda1b-6e0c63257de34c92-0"), | ||
wantBaggage: stringPtr("sentry-environment=testing,sentry-release=1.2.3,sentry-transaction=not-sampled-transaction,sentry-public_key=abc,sentry-trace_id=d4cda95b652f4a1592b449d5929fda1b"), | ||
wantSentryTrace: stringPtr("d4cda95b652f4a1592b449d5929fda1b-6e0c63257de34c92-0"), | ||
}, | ||
{ | ||
name: "should NOT set headers when traceId is empty", | ||
otelSpanContext: otelTestContext{ | ||
traceId: "", | ||
spanId: "6e0c63257de34c92", | ||
traceFlags: trace.FlagsSampled, | ||
}, | ||
sentryTransactionContext: transactionTestContext{ | ||
name: "transaction-name", | ||
traceID: "", | ||
spanID: "6e0c63257de34c92", | ||
sampled: sentry.SampledTrue, | ||
}, | ||
baggage: nil, | ||
sentryTrace: nil, | ||
wantBaggage: nil, | ||
wantSentryTrace: nil, | ||
}, | ||
{ | ||
name: "should NOT set headers when spanId is empty", | ||
otelSpanContext: otelTestContext{ | ||
traceId: "d4cda95b652f4a1592b449d5929fda1b", | ||
spanId: "", | ||
traceFlags: trace.FlagsSampled, | ||
}, | ||
sentryTransactionContext: transactionTestContext{ | ||
name: "transaction-name", | ||
traceID: "d4cda95b652f4a1592b449d5929fda1b", | ||
spanID: "", | ||
sampled: sentry.SampledTrue, | ||
}, | ||
baggage: nil, | ||
sentryTrace: nil, | ||
wantBaggage: nil, | ||
wantSentryTrace: nil, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
tt := tt | ||
t.Run(tt.name, func(t *testing.T) { | ||
propagator, carrier := setupPropagatorTest() | ||
traceId, _ := trace.TraceIDFromHex(tt.otelSpanContext.traceId) | ||
spanId, _ := trace.SpanIDFromHex(tt.otelSpanContext.spanId) | ||
otelSpanContext := trace.NewSpanContext(trace.SpanContextConfig{ | ||
TraceID: traceId, | ||
SpanID: spanId, | ||
TraceFlags: tt.otelSpanContext.traceFlags, | ||
}) | ||
testContextConfig := createTransactionAndMaybeSpan(tt.sentryTransactionContext, withChildSpan) | ||
otelSpanContext := trace.NewSpanContext(testContextConfig) | ||
ctx := trace.ContextWithSpanContext(context.Background(), otelSpanContext) | ||
createTransactionAndMaybeSpan(tt.sentryTransactionContext, false) | ||
|
||
propagator.Inject(ctx, carrier) | ||
|
||
expectedCarrier := propagation.MapCarrier{} | ||
if tt.baggage != nil { | ||
expectedCarrier["baggage"] = *tt.baggage | ||
if tt.wantBaggage != nil { | ||
expectedCarrier["baggage"] = *tt.wantBaggage | ||
} | ||
if tt.sentryTrace != nil { | ||
expectedCarrier["sentry-trace"] = *tt.sentryTrace | ||
if tt.wantSentryTrace != nil { | ||
expectedCarrier["sentry-trace"] = *tt.wantSentryTrace | ||
} | ||
assertMapCarrierEqual(t, carrier, expectedCarrier) | ||
}) | ||
} | ||
} | ||
|
||
func TestInjectUsesSetsValidTraceFromTransaction(t *testing.T) { | ||
testInjectUsesSetsValidTrace(t, false) | ||
} | ||
|
||
func TestInjectUsesSetsValidTraceFromChildSpan(t *testing.T) { | ||
testInjectUsesSetsValidTrace(t, true) | ||
} | ||
|
||
/// Extract | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a helper function that's used in multiple places already, do we really want this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Up to you, no strong feelings.