Skip to content

Commit 1f5013a

Browse files
mydeas1gr1d
authored andcommitted
fix(v8/core): Use consistent continueTrace implementation in core (#14813)
We have a different implementation of `continueTrace` for OTEL/Node. Until now we relied on actually using the import from `@sentry/node` vs `@sentry/core` to ensure this. However, this is a footgun, and actually lead to a problem in NextJS because we used the core variant there. Also, it is simply not isomorphic. So to fix this, this PR puts `continueTrace` on the ACS so we can consistently use the core variant in all environments. Fixes #14787
1 parent d3f52cf commit 1f5013a

File tree

16 files changed

+50
-49
lines changed

16 files changed

+50
-49
lines changed

packages/astro/src/index.types.ts

-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ export declare function flush(timeout?: number | undefined): PromiseLike<boolean
2727

2828
// eslint-disable-next-line deprecation/deprecation
2929
export declare const getCurrentHub: typeof clientSdk.getCurrentHub;
30-
export declare const getClient: typeof clientSdk.getClient;
31-
export declare const continueTrace: typeof clientSdk.continueTrace;
3230

3331
export declare const Span: clientSdk.Span;
3432

packages/core/src/asyncContext/types.ts

+8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Scope } from '../types-hoist';
22
import type { getTraceData } from '../utils/traceData';
33
import type {
4+
continueTrace,
45
startInactiveSpan,
56
startSpan,
67
startSpanManual,
@@ -68,4 +69,11 @@ export interface AsyncContextStrategy {
6869

6970
/** Get trace data as serialized string values for propagation via `sentry-trace` and `baggage`. */
7071
getTraceData?: typeof getTraceData;
72+
73+
/**
74+
* Continue a trace from `sentry-trace` and `baggage` values.
75+
* These values can be obtained from incoming request headers, or in the browser from `<meta name="sentry-trace">`
76+
* and `<meta name="baggage">` HTML tags.
77+
*/
78+
continueTrace?: typeof continueTrace;
7179
}

packages/core/src/tracing/trace.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -191,15 +191,20 @@ export function startInactiveSpan(options: StartSpanOptions): Span {
191191
* be attached to the incoming trace.
192192
*/
193193
export const continueTrace = <V>(
194-
{
195-
sentryTrace,
196-
baggage,
197-
}: {
194+
options: {
198195
sentryTrace: Parameters<typeof propagationContextFromHeaders>[0];
199196
baggage: Parameters<typeof propagationContextFromHeaders>[1];
200197
},
201198
callback: () => V,
202199
): V => {
200+
const carrier = getMainCarrier();
201+
const acs = getAsyncContextStrategy(carrier);
202+
if (acs.continueTrace) {
203+
return acs.continueTrace(options, callback);
204+
}
205+
206+
const { sentryTrace, baggage } = options;
207+
203208
return withScope(scope => {
204209
const propagationContext = propagationContextFromHeaders(sentryTrace, baggage);
205210
scope.setPropagationContext(propagationContext);

packages/nextjs/src/common/pages-router-instrumentation/wrapApiHandlerWithSentry.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
44
captureException,
55
continueTrace,
6+
getActiveSpan,
67
httpRequestToRequestData,
78
isString,
89
logger,
@@ -59,7 +60,13 @@ export function wrapApiHandlerWithSentry(apiHandler: NextApiHandler, parameteriz
5960
req.__withSentry_applied__ = true;
6061

6162
return withIsolationScope(isolationScope => {
62-
return continueTrace(
63+
// Normally, there is an active span here (from Next.js OTEL) and we just use that as parent
64+
// Else, we manually continueTrace from the incoming headers
65+
const continueTraceIfNoActiveSpan = getActiveSpan()
66+
? <T>(_opts: unknown, callback: () => T) => callback()
67+
: continueTrace;
68+
69+
return continueTraceIfNoActiveSpan(
6370
{
6471
sentryTrace:
6572
req.headers && isString(req.headers['sentry-trace']) ? req.headers['sentry-trace'] : undefined,

packages/nextjs/src/common/withServerActionInstrumentation.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { RequestEventData } from '@sentry/core';
2+
import { getActiveSpan } from '@sentry/core';
23
import {
34
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
45
SPAN_STATUS_ERROR,
@@ -95,7 +96,13 @@ async function withServerActionInstrumentationImplementation<A extends (...args:
9596
} satisfies RequestEventData,
9697
});
9798

98-
return continueTrace(
99+
// Normally, there is an active span here (from Next.js OTEL) and we just use that as parent
100+
// Else, we manually continueTrace from the incoming headers
101+
const continueTraceIfNoActiveSpan = getActiveSpan()
102+
? <T>(_opts: unknown, callback: () => T) => callback()
103+
: continueTrace;
104+
105+
return continueTraceIfNoActiveSpan(
99106
{
100107
sentryTrace: sentryTraceHeader,
101108
baggage: baggageHeader,

packages/nextjs/src/index.types.ts

-4
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ export declare function init(
1919
options: Options | clientSdk.BrowserOptions | serverSdk.NodeOptions | edgeSdk.EdgeOptions,
2020
): Client | undefined;
2121

22-
export declare const getClient: typeof clientSdk.getClient;
23-
export declare const getRootSpan: typeof serverSdk.getRootSpan;
24-
export declare const continueTrace: typeof clientSdk.continueTrace;
25-
2622
export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration;
2723
export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration;
2824

packages/node/src/index.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,6 @@ export { addRequestDataToEvent, DEFAULT_USER_INCLUDES, extractRequestData } from
6363
export {
6464
// eslint-disable-next-line deprecation/deprecation
6565
addOpenTelemetryInstrumentation,
66-
// These are custom variants that need to be used instead of the core one
67-
// As they have slightly different implementations
68-
continueTrace,
6966
// This needs exporting so the NodeClient can be used without calling init
7067
setOpenTelemetryContextAsyncContextStrategy as setNodeAsyncContextStrategy,
7168
} from '@sentry/opentelemetry';
@@ -110,6 +107,7 @@ export {
110107
getIsolationScope,
111108
getTraceData,
112109
getTraceMetaTags,
110+
continueTrace,
113111
withScope,
114112
withIsolationScope,
115113
captureException,

packages/nuxt/src/index.types.ts

-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,5 @@ export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsInteg
1414
export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration;
1515
export declare const getDefaultIntegrations: (options: Options) => Integration[];
1616
export declare const defaultStackParser: StackParser;
17-
export declare const continueTrace: typeof clientSdk.continueTrace;
1817
// eslint-disable-next-line deprecation/deprecation
1918
export declare const metrics: typeof clientSdk.metrics & typeof serverSdk.metrics;

packages/opentelemetry/src/asyncContextStrategy.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
SENTRY_FORK_SET_ISOLATION_SCOPE_CONTEXT_KEY,
77
SENTRY_FORK_SET_SCOPE_CONTEXT_KEY,
88
} from './constants';
9-
import { startInactiveSpan, startSpan, startSpanManual, withActiveSpan } from './trace';
9+
import { continueTrace, startInactiveSpan, startSpan, startSpanManual, withActiveSpan } from './trace';
1010
import type { CurrentScopes } from './types';
1111
import { getScopesFromContext } from './utils/contextData';
1212
import { getActiveSpan } from './utils/getActiveSpan';
@@ -103,6 +103,7 @@ export function setOpenTelemetryContextAsyncContextStrategy(): void {
103103
getActiveSpan,
104104
suppressTracing,
105105
getTraceData,
106+
continueTrace,
106107
// The types here don't fully align, because our own `Span` type is narrower
107108
// than the OTEL one - but this is OK for here, as we now we'll only have OTEL spans passed around
108109
withActiveSpan: withActiveSpan as typeof defaultWithActiveSpan,

packages/opentelemetry/src/trace.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import type { Context, Span, SpanContext, SpanOptions, Tracer } from '@opentelemetry/api';
22
import { SpanStatusCode, TraceFlags, context, trace } from '@opentelemetry/api';
33
import { suppressTracing } from '@opentelemetry/core';
4-
import type { Client, DynamicSamplingContext, Scope, Span as SentrySpan, TraceContext } from '@sentry/core';
4+
import type {
5+
Client,
6+
DynamicSamplingContext,
7+
Scope,
8+
Span as SentrySpan,
9+
TraceContext,
10+
continueTrace as baseContinueTrace,
11+
} from '@sentry/core';
512
import {
613
SDK_VERSION,
714
SEMANTIC_ATTRIBUTE_SENTRY_OP,
8-
continueTrace as baseContinueTrace,
915
getClient,
1016
getCurrentScope,
1117
getDynamicSamplingContextFromScope,
@@ -247,9 +253,7 @@ function getContextForScope(scope?: Scope): Context {
247253
* It propagates the trace as a remote span, in addition to setting it on the propagation context.
248254
*/
249255
export function continueTrace<T>(options: Parameters<typeof baseContinueTrace>[0], callback: () => T): T {
250-
return baseContinueTrace(options, () => {
251-
return continueTraceAsRemoteSpan(context.active(), options, callback);
252-
});
256+
return continueTraceAsRemoteSpan(context.active(), options, callback);
253257
}
254258

255259
/**

packages/opentelemetry/test/trace.test.ts

+3-19
Original file line numberDiff line numberDiff line change
@@ -1574,11 +1574,8 @@ describe('continueTrace', () => {
15741574
);
15751575

15761576
expect(scope.getPropagationContext()).toEqual({
1577-
dsc: {}, // DSC should be an empty object (frozen), because there was an incoming trace
1578-
sampled: false,
1579-
parentSpanId: '1121201211212012',
15801577
spanId: expect.any(String),
1581-
traceId: '12312012123120121231201212312012',
1578+
traceId: expect.any(String),
15821579
});
15831580

15841581
expect(scope.getScopeData().sdkProcessingMetadata).toEqual({});
@@ -1605,14 +1602,8 @@ describe('continueTrace', () => {
16051602
);
16061603

16071604
expect(scope.getPropagationContext()).toEqual({
1608-
dsc: {
1609-
environment: 'production',
1610-
version: '1.0',
1611-
},
1612-
sampled: true,
1613-
parentSpanId: '1121201211212012',
16141605
spanId: expect.any(String),
1615-
traceId: '12312012123120121231201212312012',
1606+
traceId: expect.any(String),
16161607
});
16171608

16181609
expect(scope.getScopeData().sdkProcessingMetadata).toEqual({});
@@ -1639,16 +1630,9 @@ describe('continueTrace', () => {
16391630
);
16401631

16411632
expect(scope.getPropagationContext()).toEqual({
1642-
dsc: {
1643-
environment: 'production',
1644-
version: '1.0',
1645-
},
1646-
sampled: true,
1647-
parentSpanId: '1121201211212012',
16481633
spanId: expect.any(String),
1649-
traceId: '12312012123120121231201212312012',
1634+
traceId: expect.any(String),
16501635
});
1651-
16521636
expect(scope.getScopeData().sdkProcessingMetadata).toEqual({});
16531637
});
16541638

packages/remix/src/index.types.ts

-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ declare const runtime: 'client' | 'server';
3232

3333
// eslint-disable-next-line deprecation/deprecation
3434
export declare const getCurrentHub: typeof clientSdk.getCurrentHub;
35-
export declare const getClient: typeof clientSdk.getClient;
36-
export declare const continueTrace: typeof clientSdk.continueTrace;
3735

3836
export const close = runtime === 'client' ? clientSdk.close : serverSdk.close;
3937
export const flush = runtime === 'client' ? clientSdk.flush : serverSdk.flush;

packages/remix/src/utils/instrumentServer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
SEMANTIC_ATTRIBUTE_SENTRY_OP,
55
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
66
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
7+
continueTrace,
78
fill,
89
getActiveSpan,
910
getClient,
@@ -19,7 +20,6 @@ import {
1920
winterCGRequestToRequestData,
2021
withIsolationScope,
2122
} from '@sentry/core';
22-
import { continueTrace } from '@sentry/opentelemetry';
2323
import { DEBUG_BUILD } from './debug-build';
2424
import { captureRemixServerException, errorHandleDataFunction, errorHandleDocumentRequestFunction } from './errors';
2525
import { getFutureFlagsServer, getRemixVersionFromBuild } from './futureFlags';

packages/solidstart/src/index.types.ts

-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,5 @@ export declare function close(timeout?: number | undefined): PromiseLike<boolean
2525
export declare function flush(timeout?: number | undefined): PromiseLike<boolean>;
2626
export declare function lastEventId(): string | undefined;
2727

28-
export declare const continueTrace: typeof clientSdk.continueTrace;
29-
3028
// eslint-disable-next-line deprecation/deprecation
3129
export declare const metrics: typeof clientSdk.metrics & typeof serverSdk.metrics;

packages/sveltekit/src/index.types.ts

-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@ export declare function close(timeout?: number | undefined): PromiseLike<boolean
5050
export declare function flush(timeout?: number | undefined): PromiseLike<boolean>;
5151
export declare function lastEventId(): string | undefined;
5252

53-
export declare const continueTrace: typeof clientSdk.continueTrace;
54-
5553
// eslint-disable-next-line deprecation/deprecation
5654
export declare const metrics: typeof clientSdk.metrics & typeof serverSdk.metrics;
5755

packages/sveltekit/src/server/handle.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Span } from '@sentry/core';
22
import {
33
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
44
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
5+
continueTrace,
56
getActiveSpan,
67
getCurrentScope,
78
getDefaultIsolationScope,
@@ -13,7 +14,6 @@ import {
1314
winterCGRequestToRequestData,
1415
withIsolationScope,
1516
} from '@sentry/core';
16-
import { continueTrace } from '@sentry/node';
1717
import type { Handle, ResolveOptions } from '@sveltejs/kit';
1818

1919
import { DEBUG_BUILD } from '../common/debug-build';

0 commit comments

Comments
 (0)