Skip to content

Commit 0d2c960

Browse files
authored
feat(nextjs): Skip OTEL root spans emitted by Next.js (#11623)
We now skip any OTEL spans that Next.js emits, unless we already have a parent span that was created elsewhere (e.g. by our auto instrumentation). This way, we should be able to ensure consistent root spans for all cases.
1 parent f1c4611 commit 0d2c960

File tree

1 file changed

+29
-13
lines changed

1 file changed

+29
-13
lines changed

packages/opentelemetry/src/sampler.ts

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,14 @@ export class SentrySampler implements Sampler {
6262
return { decision: SamplingDecision.NOT_RECORD, traceState };
6363
}
6464

65-
let parentSampled: boolean | undefined = undefined;
66-
67-
// Only inherit sample rate if `traceId` is the same
68-
// Note for testing: `isSpanContextValid()` checks the format of the traceId/spanId, so we need to pass valid ones
69-
if (parentSpan && parentContext && isSpanContextValid(parentContext) && parentContext.traceId === traceId) {
70-
if (parentContext.isRemote) {
71-
parentSampled = getParentRemoteSampled(parentSpan);
72-
DEBUG_BUILD &&
73-
logger.log(`[Tracing] Inheriting remote parent's sampled decision for ${spanName}: ${parentSampled}`);
74-
} else {
75-
parentSampled = getSamplingDecision(parentContext);
76-
DEBUG_BUILD && logger.log(`[Tracing] Inheriting parent's sampled decision for ${spanName}: ${parentSampled}`);
77-
}
65+
const parentSampled = parentSpan ? getParentSampled(parentSpan, traceId, spanName) : undefined;
66+
67+
// If we encounter a span emitted by Next.js, we do not want to sample it
68+
// The reason for this is that the data quality of the spans varies, it is different per version of Next,
69+
// and we need to keep our manual instrumentation around for the edge runtime anyhow.
70+
// BUT we only do this if we don't have a parent span with a sampling decision yet
71+
if (spanAttributes['next.span_type'] && typeof parentSampled !== 'boolean') {
72+
return { decision: SamplingDecision.NOT_RECORD, traceState: traceState };
7873
}
7974

8075
const [sampled, sampleRate] = sampleSpan(options, {
@@ -129,3 +124,24 @@ function getParentRemoteSampled(parentSpan: Span): boolean | undefined {
129124
// Only inherit sampled if `traceId` is the same
130125
return traceparentData && traceId === traceparentData.traceId ? traceparentData.sampled : undefined;
131126
}
127+
128+
function getParentSampled(parentSpan: Span, traceId: string, spanName: string): boolean | undefined {
129+
const parentContext = parentSpan.spanContext();
130+
131+
// Only inherit sample rate if `traceId` is the same
132+
// Note for testing: `isSpanContextValid()` checks the format of the traceId/spanId, so we need to pass valid ones
133+
if (isSpanContextValid(parentContext) && parentContext.traceId === traceId) {
134+
if (parentContext.isRemote) {
135+
const parentSampled = getParentRemoteSampled(parentSpan);
136+
DEBUG_BUILD &&
137+
logger.log(`[Tracing] Inheriting remote parent's sampled decision for ${spanName}: ${parentSampled}`);
138+
return parentSampled;
139+
}
140+
141+
const parentSampled = getSamplingDecision(parentContext);
142+
DEBUG_BUILD && logger.log(`[Tracing] Inheriting parent's sampled decision for ${spanName}: ${parentSampled}`);
143+
return parentSampled;
144+
}
145+
146+
return undefined;
147+
}

0 commit comments

Comments
 (0)