Skip to content

Commit 5cf5d09

Browse files
committed
re-export span api
1 parent f7fb5c4 commit 5cf5d09

File tree

4 files changed

+99
-0
lines changed

4 files changed

+99
-0
lines changed

packages/nextjs/src/client/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import { applyTunnelRouteOption } from './tunnelRoute';
1414
export * from '@sentry/react';
1515
export * from '../common';
1616
export { captureUnderscoreErrorException } from '../common/pages-router-instrumentation/_error';
17+
18+
// Override core span methods with Next.js-specific implementations that support Cache Components
19+
export { startSpan, startSpanManual, startInactiveSpan } from '../common/utils/nextSpan';
1720
export { browserTracingIntegration } from './browserTracingIntegration';
1821
export { captureRouterTransitionStart } from './routing/appRouterRoutingInstrumentation';
1922

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import type { Span, StartSpanOptions } from '@sentry/core';
2+
import {
3+
SentryNonRecordingSpan,
4+
startInactiveSpan as coreStartInactiveSpan,
5+
startSpan as coreStartSpan,
6+
startSpanManual as coreStartSpanManual,
7+
} from '@sentry/core';
8+
9+
/**
10+
* Check if we're currently in a Next.js Cache Components context.
11+
* Cache Components are rendered during the production build phase.
12+
*
13+
* @returns true if we're in a Cache Components context, false otherwise
14+
* // todo: This is a heuristic check, we should use a more reliable way to detect a Cache Components context once Vercel exposes it.
15+
*/
16+
function isCacheComponentContext(): boolean {
17+
return process.env.NEXT_PHASE === 'phase-production-build';
18+
}
19+
20+
/**
21+
* Next.js-specific implementation of `startSpan` that skips span creation
22+
* in Cache Components contexts (which render at build time).
23+
*
24+
* When in a Cache Components context, we execute the callback with a non-recording span
25+
* and return early without creating an actual span, since spans don't make sense at build time.
26+
*
27+
* @param options - Options for starting the span
28+
* @param callback - Callback function that receives the span
29+
* @returns The return value of the callback
30+
*/
31+
export function startSpan<T>(options: StartSpanOptions, callback: (span: Span) => T): T {
32+
if (isCacheComponentContext()) {
33+
// Cache Components render at build time, so spans don't make sense
34+
// Execute callback with a non-recording span (no crypto calls) and return early
35+
// Use placeholder IDs since this span won't be sent to Sentry anyway
36+
const nonRecordingSpan = new SentryNonRecordingSpan({
37+
traceId: '00000000000000000000000000000000',
38+
spanId: '0000000000000000',
39+
});
40+
return callback(nonRecordingSpan);
41+
}
42+
43+
return coreStartSpan(options, callback);
44+
}
45+
46+
/**
47+
*
48+
* When in a Cache Components context, we execute the callback with a non-recording span
49+
* and return early without creating an actual span, since spans don't make sense at build time.
50+
*
51+
* @param options - Options for starting the span
52+
* @param callback - Callback function that receives the span and finish function
53+
* @returns The return value of the callback
54+
*/
55+
export function startSpanManual<T>(options: StartSpanOptions, callback: (span: Span, finish: () => void) => T): T {
56+
if (isCacheComponentContext()) {
57+
// Cache Components render at build time, so spans don't make sense
58+
// Execute callback with a non-recording span (no crypto calls) and return early
59+
// Use placeholder IDs since this span won't be sent to Sentry anyway
60+
const nonRecordingSpan = new SentryNonRecordingSpan({
61+
traceId: '00000000000000000000000000000000',
62+
spanId: '0000000000000000',
63+
});
64+
return callback(nonRecordingSpan, () => nonRecordingSpan.end());
65+
}
66+
67+
return coreStartSpanManual(options, callback);
68+
}
69+
70+
/**
71+
*
72+
* When in a Cache Components context, we return a non-recording span and return early
73+
* without creating an actual span, since spans don't make sense at build time.
74+
*
75+
* @param options - Options for starting the span
76+
* @returns A non-recording span (in Cache Components context) or the created span
77+
*/
78+
export function startInactiveSpan(options: StartSpanOptions): Span {
79+
if (isCacheComponentContext()) {
80+
// Cache Components render at build time, so spans don't make sense
81+
// Return a non-recording span (no crypto calls) and return early
82+
// Use placeholder IDs since this span won't be sent to Sentry anyway
83+
return new SentryNonRecordingSpan({
84+
traceId: '00000000000000000000000000000000',
85+
spanId: '0000000000000000',
86+
});
87+
}
88+
89+
return coreStartInactiveSpan(options);
90+
}

packages/nextjs/src/edge/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ import { distDirRewriteFramesIntegration } from './distDirRewriteFramesIntegrati
2828
export * from '@sentry/vercel-edge';
2929
export * from '../common';
3030
export { captureUnderscoreErrorException } from '../common/pages-router-instrumentation/_error';
31+
32+
// Override core span methods with Next.js-specific implementations that support Cache Components
33+
export { startSpan, startSpanManual, startInactiveSpan } from '../common/utils/nextSpan';
3134
export { wrapApiHandlerWithSentry } from './wrapApiHandlerWithSentry';
3235

3336
export type EdgeOptions = VercelEdgeOptions;

packages/nextjs/src/server/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ export * from '@sentry/node';
4646

4747
export { captureUnderscoreErrorException } from '../common/pages-router-instrumentation/_error';
4848

49+
// Override core span methods with Next.js-specific implementations that support Cache Components
50+
export { startSpan, startSpanManual, startInactiveSpan } from '../common/utils/nextSpan';
51+
4952
const globalWithInjectedValues = GLOBAL_OBJ as typeof GLOBAL_OBJ & {
5053
_sentryRewriteFramesDistDir?: string;
5154
_sentryRewritesTunnelPath?: string;

0 commit comments

Comments
 (0)