Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1132,7 +1132,9 @@ describe('browserTracingIntegration', () => {
expect(spanIsSampled(idleSpan)).toBe(false);

expect(dynamicSamplingContext).toBeDefined();
expect(dynamicSamplingContext).toStrictEqual({});
// Continuing a trace without an incoming Sentry DSC does not populate a new one, but the
// scope's `sample_rand` is still propagated so downstream sampling decisions stay consistent.
expect(dynamicSamplingContext).toStrictEqual({ sample_rand: propagationContext.sampleRand.toString() });

// Propagation context keeps the meta tag trace data for later events on the same route to add them to the trace
expect(propagationContext.traceId).toEqual('12312012123120121231201212312012');
Expand Down
10 changes: 8 additions & 2 deletions packages/core/src/tracing/trace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ function createChildOrRootSpan({

freezeDscOnSpan(span, dsc);
} else {
const { traceId, dsc, parentSpanId, sampled: parentSampled } = scope.getPropagationContext();
const { traceId, dsc, parentSpanId, sampled: parentSampled, sampleRand } = scope.getPropagationContext();

span = _startRootSpan(
{
Expand All @@ -447,7 +447,13 @@ function createChildOrRootSpan({
);

if (dsc) {
freezeDscOnSpan(span, dsc);
// A trace continued without incoming baggage carries an empty DSC (we are not the head of
// trace). Fold in the scope's `sample_rand` so it still propagates downstream and sampling
// decisions stay consistent across the trace. A populated frozen DSC (e.g. an OTel remote
// parent whose DSC came in via baggage/trace state) is left untouched.
const dscWithSampleRand =
Object.keys(dsc).length === 0 && sampleRand !== undefined ? { sample_rand: sampleRand.toString() } : dsc;
freezeDscOnSpan(span, dscWithSampleRand);
}
}

Expand Down
7 changes: 6 additions & 1 deletion packages/sveltekit/test/server-common/handle.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { EventEnvelopeHeaders, Span } from '@sentry/core';
import { HTTP_ROUTE } from '@sentry/conventions/attributes';
import {
getCapturedScopesOnSpan,
getRootSpan,
getSpanDescendants,
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
Expand Down Expand Up @@ -265,7 +266,11 @@ describe('sentryHandle', () => {
expect(_span!.spanContext().traceId).toEqual('1234567890abcdef1234567890abcdef');
expect(spanToJSON(_span!).parent_span_id).toEqual('1234567890abcdef');
expect(spanIsSampled(_span!)).toEqual(true);
expect(envelopeHeaders!.trace).toEqual({});
// Continuing a trace without incoming baggage does not populate a new DSC, but the scope's
// `sample_rand` is still propagated so downstream sampling decisions stay consistent. Assert it
// matches the scope value rather than just any number, so a freshly-minted rand would fail.
const scopeSampleRand = getCapturedScopesOnSpan(_span!).scope!.getPropagationContext().sampleRand;
expect(envelopeHeaders!.trace).toEqual({ sample_rand: scopeSampleRand.toString() });
});

it('creates a transaction with dynamic sampling context from baggage header', async () => {
Expand Down
Loading