Skip to content
Draft
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
17 changes: 1 addition & 16 deletions packages/opentelemetry/src/propagator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,24 +104,9 @@ function getContextWithRemoteActiveSpan(
return trace.setSpanContext(ctx, spanContext);
}

/**
* Takes trace strings and propagates them as a remote active span.
* This should be used in addition to `continueTrace` in OTEL-powered environments.
*/
export function continueTraceAsRemoteSpan<T>(
ctx: Context,
options: Parameters<typeof continueTrace>[0],
callback: () => T,
): T {
const ctxWithSpanContext = getContextWithRemoteActiveSpanAndScopes(ctx, options);

return context.with(ctxWithSpanContext, callback);
}

/**
* Build a context that continues an incoming trace as a remote active span, with scopes ensured.
* Unlike `continueTraceAsRemoteSpan`, this returns the context instead of running a callback within it,
* so it can be used to implement an OpenTelemetry propagator's `extract`.
* Returns the context so it can be used to implement an OpenTelemetry propagator's `extract`.
*/
function getContextWithRemoteActiveSpanAndScopes(ctx: Context, options: Parameters<typeof continueTrace>[0]): Context {
const ctxWithRemoteSpan = getContextWithRemoteActiveSpan(ctx, options);
Expand Down
52 changes: 1 addition & 51 deletions packages/opentelemetry/src/utils/getSamplingDecision.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import type { SpanContext } from '@opentelemetry/api';
import { TraceFlags } from '@opentelemetry/api';
import type { Client, Span } from '@sentry/core';
import {
baggageHeaderToDynamicSamplingContext,
getRootSpan,
hasSpansEnabled,
spanIsIgnored,
spanIsSampled,
spanIsSentrySpan,
} from '@sentry/core';
import { baggageHeaderToDynamicSamplingContext } from '@sentry/core';
import { SENTRY_TRACE_STATE_DSC, SENTRY_TRACE_STATE_SAMPLED_NOT_RECORDING } from '../constants';

/**
Expand Down Expand Up @@ -48,45 +40,3 @@ export function getSamplingDecision(spanContext: SpanContext): boolean | undefin

return undefined;
}

/**
* Resolve a span's sampling decision for trace propagation, also handling native Sentry spans.
*
* Prefer the OpenTelemetry trace state via {@link getSamplingDecision}. Native Sentry spans (created
* by the `SentryTracerProvider`) don't carry that trace state, so when it's absent we fall back to the
* span's own decision via `spanIsSampled` — but only for an *explicit* decision. An explicit decision
* originates at a real `SentrySpan` root (a negatively sampled root, or a child of one) or an ignored
* segment root. Other non-recording placeholder roots (orphan/suppressed spans or TwP placeholders)
* and remote spans have a *deferred* decision that lives elsewhere (the scope, or the incoming trace
* state), so we return `undefined` rather than wrongly asserting `-0`.
*
* TODO(v11): Once the OTel SDK provider is gone and every local span is a native Sentry span, the
* trace-state lookup only matters for remote (incoming) spans; the local path always reads the span's
* own decision, so the "native-vs-OTel-SDK span" framing can be dropped (local → span, remote → trace state).
*/
export function getSampledForPropagation(span: Span, client: Client | undefined): boolean | undefined {
const spanContext = span.spanContext();
const rootSpan = getRootSpan(span);

// Prefer the OTel trace state: it carries the decision for OTel SDK spans and for remote (incoming)
// spans, and unambiguously separates sampled / unsampled / deferred.
const samplingDecision = getSamplingDecision(spanContext);
if (samplingDecision !== undefined) {
return samplingDecision;
}

if (spanIsIgnored(rootSpan)) {
return false;
}

// No trace state in it. Only read the span's own decision (`spanIsSampled`) when it's an explicit
// one, which lives on a native recording `SentrySpan` root (created by the SentryTracerProvider).
// Everything else defers: TwP (deferred), remote spans (decision is in the incoming trace state),
// and non-recording placeholder roots — whether a Sentry orphan/suppressed span or, on the OTel SDK
// path, an OpenTelemetry `NonRecordingSpan` (which `spanIsSentrySpan` also excludes).
if (!hasSpansEnabled(client?.getOptions()) || spanContext.isRemote || !spanIsSentrySpan(rootSpan)) {
return undefined;
}

return spanIsSampled(span);
}
Loading