From 4f3629325dc06d2b0777f0c4cd6fb23c5e3428d3 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Mon, 26 May 2025 10:25:34 +0200 Subject: [PATCH] fix(opentelemetry): Ensure `_getTraceInfoFromScope` works Also ensure that `withScope(scope, callback)` maintains the active span from the passed in scope. --- packages/core/src/client.ts | 19 ++++++++++--------- packages/core/test/lib/tracing/trace.test.ts | 13 +++++++++++++ .../opentelemetry/src/asyncContextStrategy.ts | 4 ++-- packages/opentelemetry/test/trace.test.ts | 15 +++++++++++++++ 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 0dda6c86fd26..d9abd8f5d0d2 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -1,7 +1,7 @@ /* eslint-disable max-lines */ import { getEnvelopeEndpointWithUrlEncodedAuth } from './api'; import { DEFAULT_ENVIRONMENT } from './constants'; -import { getCurrentScope, getIsolationScope, getTraceContextFromScope } from './currentScopes'; +import { getCurrentScope, getIsolationScope, getTraceContextFromScope, withScope } from './currentScopes'; import { DEBUG_BUILD } from './debug-build'; import { createEventEnvelope, createSessionEnvelope } from './envelope'; import type { IntegrationIndex } from './integration'; @@ -36,8 +36,7 @@ import { getPossibleEventMessages } from './utils/eventUtils'; import { merge } from './utils/merge'; import { parseSampleRate } from './utils/parseSampleRate'; import { prepareEvent } from './utils/prepareEvent'; -import { _getSpanForScope } from './utils/spanOnScope'; -import { showSpanDropWarning, spanToTraceContext } from './utils/spanUtils'; +import { getActiveSpan, showSpanDropWarning, spanToTraceContext } from './utils/spanUtils'; import { convertSpanJsonToTransactionEvent, convertTransactionEventToSpanJson } from './utils/transactionEvent'; import { createClientReportEnvelope } from './utils-hoist/clientreport'; import { dsnToString, makeDsn } from './utils-hoist/dsn'; @@ -1325,10 +1324,12 @@ export function _getTraceInfoFromScope( return [undefined, undefined]; } - const span = _getSpanForScope(scope); - const traceContext = span ? spanToTraceContext(span) : getTraceContextFromScope(scope); - const dynamicSamplingContext = span - ? getDynamicSamplingContextFromSpan(span) - : getDynamicSamplingContextFromScope(client, scope); - return [dynamicSamplingContext, traceContext]; + return withScope(scope, () => { + const span = getActiveSpan(); + const traceContext = span ? spanToTraceContext(span) : getTraceContextFromScope(scope); + const dynamicSamplingContext = span + ? getDynamicSamplingContextFromSpan(span) + : getDynamicSamplingContextFromScope(client, scope); + return [dynamicSamplingContext, traceContext]; + }); } diff --git a/packages/core/test/lib/tracing/trace.test.ts b/packages/core/test/lib/tracing/trace.test.ts index 0e0d16fb6ec4..83f4b150a6d6 100644 --- a/packages/core/test/lib/tracing/trace.test.ts +++ b/packages/core/test/lib/tracing/trace.test.ts @@ -1781,6 +1781,19 @@ describe('getActiveSpan', () => { const result = getActiveSpan(); expect(result).toBe(staticSpan); }); + + it('handles active span when passing scopes to withScope', () => { + const [scope, span] = startSpan({ name: 'outer' }, span => { + return [getCurrentScope(), span]; + }); + + const spanOnScope = withScope(scope, () => { + return getActiveSpan(); + }); + + expect(spanOnScope).toBeDefined(); + expect(spanOnScope).toBe(span); + }); }); describe('withActiveSpan()', () => { diff --git a/packages/opentelemetry/src/asyncContextStrategy.ts b/packages/opentelemetry/src/asyncContextStrategy.ts index 695175bc3fa1..9f7b38d0b43d 100644 --- a/packages/opentelemetry/src/asyncContextStrategy.ts +++ b/packages/opentelemetry/src/asyncContextStrategy.ts @@ -8,7 +8,7 @@ import { } from './constants'; import { continueTrace, startInactiveSpan, startSpan, startSpanManual, withActiveSpan } from './trace'; import type { CurrentScopes } from './types'; -import { getScopesFromContext } from './utils/contextData'; +import { getContextFromScope, getScopesFromContext } from './utils/contextData'; import { getActiveSpan } from './utils/getActiveSpan'; import { getTraceData } from './utils/getTraceData'; import { suppressTracing } from './utils/suppressTracing'; @@ -48,7 +48,7 @@ export function setOpenTelemetryContextAsyncContextStrategy(): void { } function withSetScope(scope: Scope, callback: (scope: Scope) => T): T { - const ctx = api.context.active(); + const ctx = getContextFromScope(scope) || api.context.active(); // We depend on the otelContextManager to handle the context/hub // We set the `SENTRY_FORK_SET_SCOPE_CONTEXT_KEY` context value, which is picked up by diff --git a/packages/opentelemetry/test/trace.test.ts b/packages/opentelemetry/test/trace.test.ts index 74b37809f4ae..f9aed823a4a4 100644 --- a/packages/opentelemetry/test/trace.test.ts +++ b/packages/opentelemetry/test/trace.test.ts @@ -1342,6 +1342,21 @@ describe('trace', () => { }); }); }); + + describe('scope passing', () => { + it('handles active span when passing scopes to withScope', () => { + const [scope, span] = startSpan({ name: 'outer' }, span => { + return [getCurrentScope(), span]; + }); + + const spanOnScope = withScope(scope, () => { + return getActiveSpan(); + }); + + expect(spanOnScope).toBeDefined(); + expect(spanOnScope).toBe(span); + }); + }); }); describe('trace (tracing disabled)', () => {