Skip to content

Commit 576a1ad

Browse files
s1gr1dmydea
andauthored
fix(v8/core): Use consistent continueTrace implementation in core (#14819)
V8 backport of #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 Co-authored-by: Francesco Gringl-Novy <francesco.novy@sentry.io>
1 parent 75ca8b9 commit 576a1ad

File tree

16 files changed

+50
-52
lines changed

16 files changed

+50
-52
lines changed

packages/astro/src/index.types.ts

Lines changed: 0 additions & 2 deletions
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

Lines changed: 8 additions & 0 deletions
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

Lines changed: 9 additions & 4 deletions
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

Lines changed: 8 additions & 1 deletion
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

Lines changed: 8 additions & 1 deletion
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

Lines changed: 0 additions & 4 deletions
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

Lines changed: 1 addition & 3 deletions
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

Lines changed: 0 additions & 1 deletion
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 9 additions & 5 deletions
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
/**

0 commit comments

Comments
 (0)