From 7fcbddc8175a2af25bd288d0f7c69a6371b0ac3d Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 4 Dec 2023 15:42:55 +0000 Subject: [PATCH 1/3] fix(nextjs): Don't propagate headers if we detect static behaviour --- packages/nextjs/rollup.npm.config.js | 2 + packages/nextjs/src/common/types.ts | 16 ++++++-- .../src/common/utils/hasStaticBehaviour.ts | 14 +++++++ .../src/common/wrapRouteHandlerWithSentry.ts | 13 +++--- .../common/wrapServerComponentWithSentry.ts | 8 ++-- .../src/config/loaders/wrappingLoader.ts | 30 ++++++++++++-- .../templates/routeHandlerWrapperTemplate.ts | 17 ++++++++ .../serverComponentWrapperTemplate.ts | 20 ++++++++++ .../staticGenerationAsyncStorageShim.ts | 6 +++ packages/nextjs/src/config/webpack.ts | 40 +++++++++++++------ 10 files changed, 136 insertions(+), 30 deletions(-) create mode 100644 packages/nextjs/src/common/utils/hasStaticBehaviour.ts create mode 100644 packages/nextjs/src/config/templates/staticGenerationAsyncStorageShim.ts diff --git a/packages/nextjs/rollup.npm.config.js b/packages/nextjs/rollup.npm.config.js index e033fd6f90c1..cc5c477cd727 100644 --- a/packages/nextjs/rollup.npm.config.js +++ b/packages/nextjs/rollup.npm.config.js @@ -28,6 +28,7 @@ export default [ 'src/config/templates/middlewareWrapperTemplate.ts', 'src/config/templates/pageWrapperTemplate.ts', 'src/config/templates/requestAsyncStorageShim.ts', + 'src/config/templates/staticGenerationAsyncStorageShim.ts', 'src/config/templates/sentryInitWrapperTemplate.ts', 'src/config/templates/serverComponentWrapperTemplate.ts', 'src/config/templates/routeHandlerWrapperTemplate.ts', @@ -52,6 +53,7 @@ export default [ '__SENTRY_CONFIG_IMPORT_PATH__', '__SENTRY_WRAPPING_TARGET_FILE__', '__SENTRY_NEXTJS_REQUEST_ASYNC_STORAGE_SHIM__', + '__SENTRY_NEXTJS_STATIC_GENERATION_ASYNC_STORAGE_SHIM__', ], }, }), diff --git a/packages/nextjs/src/common/types.ts b/packages/nextjs/src/common/types.ts index 9e3218959b34..fbfd6f78cec1 100644 --- a/packages/nextjs/src/common/types.ts +++ b/packages/nextjs/src/common/types.ts @@ -15,13 +15,10 @@ export type ServerComponentContext = { */ baggageHeader?: string; headers?: WebFetchHeaders; + hasStaticBehaviour?: boolean; }; export interface RouteHandlerContext { - // TODO(v8): Remove - /** - * @deprecated The SDK will automatically pick up the method from the incoming Request object instead. - */ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'; parameterizedRoute: string; // TODO(v8): Remove @@ -34,6 +31,7 @@ export interface RouteHandlerContext { * @deprecated The SDK will automatically pick up the `baggage` header from the incoming Request object instead. */ baggageHeader?: string; + hasStaticBehaviour?: boolean; } export type VercelCronsConfig = { path?: string; schedule?: string }[] | undefined; @@ -85,3 +83,13 @@ export type AugmentedNextApiResponse = NextApiResponse & { export type ResponseEndMethod = AugmentedNextApiResponse['end']; export type WrappedResponseEndMethod = AugmentedNextApiResponse['end'] & WrappedFunction; + +export interface StaticGenerationStore { + forceStatic?: boolean; + dynamicShouldError?: boolean; + isStaticGeneration?: boolean; + experimental?: { + ppr?: boolean; + }; + ppr?: boolean; +} diff --git a/packages/nextjs/src/common/utils/hasStaticBehaviour.ts b/packages/nextjs/src/common/utils/hasStaticBehaviour.ts new file mode 100644 index 000000000000..4df3aa8cc714 --- /dev/null +++ b/packages/nextjs/src/common/utils/hasStaticBehaviour.ts @@ -0,0 +1,14 @@ +import type { StaticGenerationStore } from '../types'; + +/** + * Takes a Next.js StaticGenerationStore and determines whether the current state of it would lead to static component/page behaviour. + */ +export function storeHasStaticBehaviour(staticGenerationStore: StaticGenerationStore): boolean { + return !!( + staticGenerationStore?.forceStatic || + staticGenerationStore?.isStaticGeneration || + staticGenerationStore?.dynamicShouldError || + staticGenerationStore?.experimental?.ppr || + staticGenerationStore?.ppr + ); +} diff --git a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts index c03bb3db0dbf..9eee5b9beddf 100644 --- a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts +++ b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts @@ -15,7 +15,7 @@ export function wrapRouteHandlerWithSentry any>( ): (...args: Parameters) => ReturnType extends Promise ? ReturnType : Promise> { addTracingExtensions(); // eslint-disable-next-line deprecation/deprecation - const { method, parameterizedRoute, baggageHeader, sentryTraceHeader } = context; + const { method, parameterizedRoute, baggageHeader, sentryTraceHeader, hasStaticBehaviour } = context; return new Proxy(routeHandler, { apply: (originalFunction, thisArg, args) => { return runWithAsyncContext(async () => { @@ -23,15 +23,14 @@ export function wrapRouteHandlerWithSentry any>( const currentScope = hub.getScope(); let req: Request | undefined; - let reqMethod: string | undefined; - if (args[0] instanceof Request) { + // We are not allowed to access the Request object when a route has static behavious. Otherwise Next.js will throw. + if (args[0] instanceof Request && !hasStaticBehaviour) { req = args[0]; - reqMethod = req.method; } const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( - sentryTraceHeader, - baggageHeader, + sentryTraceHeader ?? req?.headers.get('sentry-trace') ?? undefined, + baggageHeader ?? req?.headers.get('baggage') ?? undefined, ); currentScope.setPropagationContext(propagationContext); @@ -40,7 +39,7 @@ export function wrapRouteHandlerWithSentry any>( res = await trace( { op: 'http.server', - name: `${reqMethod ?? method} ${parameterizedRoute}`, + name: `${method} ${parameterizedRoute}`, status: 'ok', ...traceparentData, metadata: { diff --git a/packages/nextjs/src/common/wrapServerComponentWithSentry.ts b/packages/nextjs/src/common/wrapServerComponentWithSentry.ts index beb17980de5e..d43d7d1189f8 100644 --- a/packages/nextjs/src/common/wrapServerComponentWithSentry.ts +++ b/packages/nextjs/src/common/wrapServerComponentWithSentry.ts @@ -20,7 +20,7 @@ export function wrapServerComponentWithSentry any> context: ServerComponentContext, ): F { addTracingExtensions(); - const { componentRoute, componentType } = context; + const { componentRoute, componentType, hasStaticBehaviour } = context; // Even though users may define server components as async functions, for the client bundles // Next.js will turn them into synchronous functions and it will transform any `await`s into instances of the `use` @@ -33,9 +33,9 @@ export function wrapServerComponentWithSentry any> let maybePromiseResult; - const completeHeadersDict: Record = context.headers - ? winterCGHeadersToDict(context.headers) - : {}; + const completeHeadersDict: Record = + // We should not use any headers whent the component has static behaviour + !hasStaticBehaviour && context.headers ? winterCGHeadersToDict(context.headers) : {}; const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( // eslint-disable-next-line deprecation/deprecation diff --git a/packages/nextjs/src/config/loaders/wrappingLoader.ts b/packages/nextjs/src/config/loaders/wrappingLoader.ts index a6b852af8b28..ea35e8f1a101 100644 --- a/packages/nextjs/src/config/loaders/wrappingLoader.ts +++ b/packages/nextjs/src/config/loaders/wrappingLoader.ts @@ -24,7 +24,8 @@ const pageWrapperTemplateCode = fs.readFileSync(pageWrapperTemplatePath, { encod const middlewareWrapperTemplatePath = path.resolve(__dirname, '..', 'templates', 'middlewareWrapperTemplate.js'); const middlewareWrapperTemplateCode = fs.readFileSync(middlewareWrapperTemplatePath, { encoding: 'utf8' }); -let showedMissingAsyncStorageModuleWarning = false; +let showedMissingRequestAsyncStorageModuleWarning = false; +let showedMissingStaticGenerationAsyncStorageModuleWarning = false; const sentryInitWrapperTemplatePath = path.resolve(__dirname, '..', 'templates', 'sentryInitWrapperTemplate.js'); const sentryInitWrapperTemplateCode = fs.readFileSync(sentryInitWrapperTemplatePath, { encoding: 'utf8' }); @@ -49,6 +50,7 @@ export type WrappingLoaderOptions = { sentryConfigFilePath?: string; vercelCronsConfig?: VercelCronsConfig; nextjsRequestAsyncStorageModulePath?: string; + nextjsStaticGenerationAsyncStorageModulePath?: string; }; /** @@ -73,6 +75,7 @@ export default function wrappingLoader( sentryConfigFilePath, vercelCronsConfig, nextjsRequestAsyncStorageModulePath, + nextjsStaticGenerationAsyncStorageModulePath, } = 'getOptions' in this ? this.getOptions() : this.query; this.async(); @@ -190,14 +193,14 @@ export default function wrappingLoader( nextjsRequestAsyncStorageModulePath, ); } else { - if (!showedMissingAsyncStorageModuleWarning) { + if (!showedMissingRequestAsyncStorageModuleWarning) { // eslint-disable-next-line no-console console.warn( `${chalk.yellow('warn')} - The Sentry SDK could not access the ${chalk.bold.cyan( 'RequestAsyncStorage', )} module. Certain features may not work. There is nothing you can do to fix this yourself, but future SDK updates may resolve this.\n`, ); - showedMissingAsyncStorageModuleWarning = true; + showedMissingRequestAsyncStorageModuleWarning = true; } templateCode = templateCode.replace( /__SENTRY_NEXTJS_REQUEST_ASYNC_STORAGE_SHIM__/g, @@ -205,6 +208,27 @@ export default function wrappingLoader( ); } + if (nextjsStaticGenerationAsyncStorageModulePath !== undefined) { + templateCode = templateCode.replace( + /__SENTRY_NEXTJS_STATIC_GENERATION_ASYNC_STORAGE_SHIM__/g, + nextjsStaticGenerationAsyncStorageModulePath, + ); + } else { + if (!showedMissingStaticGenerationAsyncStorageModuleWarning) { + // eslint-disable-next-line no-console + console.warn( + `${chalk.yellow('warn')} - The Sentry SDK could not access the ${chalk.bold.cyan( + 'StaticGenerationAsyncStorage', + )} module. Certain features may not work. There is nothing you can do to fix this yourself, but future SDK updates may resolve this.\n`, + ); + showedMissingStaticGenerationAsyncStorageModuleWarning = true; + } + templateCode = templateCode.replace( + /__SENTRY_NEXTJS_STATIC_GENERATION_ASYNC_STORAGE_SHIM__/g, + '@sentry/nextjs/esm/config/templates/staticGenerationAsyncStorageShim.js', + ); + } + templateCode = templateCode.replace(/__ROUTE__/g, parameterizedPagesRoute.replace(/\\/g, '\\\\')); const componentTypeMatch = path.posix diff --git a/packages/nextjs/src/config/templates/routeHandlerWrapperTemplate.ts b/packages/nextjs/src/config/templates/routeHandlerWrapperTemplate.ts index b09d35c98b4b..ab48f106a890 100644 --- a/packages/nextjs/src/config/templates/routeHandlerWrapperTemplate.ts +++ b/packages/nextjs/src/config/templates/routeHandlerWrapperTemplate.ts @@ -2,12 +2,18 @@ import * as Sentry from '@sentry/nextjs'; // @ts-expect-error Because we cannot be sure if the RequestAsyncStorage module exists (it is not part of the Next.js public // API) we use a shim if it doesn't exist. The logic for this is in the wrapping loader. import { requestAsyncStorage } from '__SENTRY_NEXTJS_REQUEST_ASYNC_STORAGE_SHIM__'; +// @ts-expect-error Because we cannot be sure if the staticGenerationAsyncStorage module exists (it is not part of the Next.js public +// API) we use a shim if it doesn't exist. The logic for this is in the wrapping loader. +import { staticGenerationAsyncStorage } from '__SENTRY_NEXTJS_STATIC_GENERATION_ASYNC_STORAGE_SHIM__'; // @ts-expect-error See above import * as routeModule from '__SENTRY_WRAPPING_TARGET_FILE__'; +import { storeHasStaticBehaviour } from '../../common/utils/hasStaticBehaviour'; import type { RequestAsyncStorage } from './requestAsyncStorageShim'; +import type { StaticGenerationAsyncStorage } from './staticGenerationAsyncStorageShim'; declare const requestAsyncStorage: RequestAsyncStorage; +declare const staticGenerationAsyncStorage: StaticGenerationAsyncStorage; declare const routeModule: { GET?: (...args: unknown[]) => unknown; @@ -44,12 +50,23 @@ function wrapHandler(handler: T, method: 'GET' | 'POST' | 'PUT' | 'PATCH' | ' /** empty */ } + let hasStaticBehaviour: boolean | undefined = false; + try { + const staticGenerationStore = staticGenerationAsyncStorage.getStore(); + if (staticGenerationStore) { + hasStaticBehaviour = storeHasStaticBehaviour(staticGenerationStore); + } + } catch (e) { + /** empty */ + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any return Sentry.wrapRouteHandlerWithSentry(originalFunction as any, { method, parameterizedRoute: '__ROUTE__', sentryTraceHeader, baggageHeader, + hasStaticBehaviour, }).apply(thisArg, args); }, }); diff --git a/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts b/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts index d0cc4adc4466..ca40869d1e8d 100644 --- a/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts +++ b/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts @@ -8,6 +8,15 @@ import * as serverComponentModule from '__SENTRY_WRAPPING_TARGET_FILE__'; import type { RequestAsyncStorage } from './requestAsyncStorageShim'; +// @ts-expect-error Because we cannot be sure if the staticGenerationAsyncStorage module exists (it is not part of the Next.js public +// API) we use a shim if it doesn't exist. The logic for this is in the wrapping loader. +import { staticGenerationAsyncStorage } from '__SENTRY_NEXTJS_STATIC_GENERATION_ASYNC_STORAGE_SHIM__'; + +import { storeHasStaticBehaviour } from '../../common/utils/hasStaticBehaviour'; +import type { StaticGenerationAsyncStorage } from './staticGenerationAsyncStorageShim'; + +declare const staticGenerationAsyncStorage: StaticGenerationAsyncStorage; + declare const requestAsyncStorage: RequestAsyncStorage; declare const serverComponentModule: { @@ -37,12 +46,23 @@ if (typeof serverComponent === 'function') { /** empty */ } + let hasStaticBehaviour: boolean | undefined = false; + try { + const staticGenerationStore = staticGenerationAsyncStorage.getStore(); + if (staticGenerationStore) { + hasStaticBehaviour = storeHasStaticBehaviour(staticGenerationStore); + } + } catch (e) { + /** empty */ + } + return Sentry.wrapServerComponentWithSentry(originalFunction, { componentRoute: '__ROUTE__', componentType: '__COMPONENT_TYPE__', sentryTraceHeader, baggageHeader, headers, + hasStaticBehaviour, }).apply(thisArg, args); }, }); diff --git a/packages/nextjs/src/config/templates/staticGenerationAsyncStorageShim.ts b/packages/nextjs/src/config/templates/staticGenerationAsyncStorageShim.ts new file mode 100644 index 000000000000..e2a7f1357a00 --- /dev/null +++ b/packages/nextjs/src/config/templates/staticGenerationAsyncStorageShim.ts @@ -0,0 +1,6 @@ +import type { StaticGenerationStore } from '../../common/types'; + +// Vendored from https://github.com/vercel/next.js/blob/445e70502834540d476b8eeaed0228241acd92eb/packages/next/src/client/components/static-generation-async-storage.external.ts +export interface StaticGenerationAsyncStorage { + getStore: () => StaticGenerationStore | undefined; +} diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index 6f9b27d56423..3306170457d2 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -41,6 +41,24 @@ let showedMissingProjectSlugErrorMsg = false; let showedHiddenSourceMapsWarningMsg = false; let showedMissingCliBinaryWarningMsg = false; +const POTENTIAL_REQUEST_ASNYC_STORAGE_LOCATIONS = [ + // Original location of RequestAsyncStorage + // https://github.com/vercel/next.js/blob/46151dd68b417e7850146d00354f89930d10b43b/packages/next/src/client/components/request-async-storage.ts + 'next/dist/client/components/request-async-storage.js', + // Introduced in Next.js 13.4.20 + // https://github.com/vercel/next.js/blob/e1bc270830f2fc2df3542d4ef4c61b916c802df3/packages/next/src/client/components/request-async-storage.external.ts + 'next/dist/client/components/request-async-storage.external.js', +]; + +const POTENTIAL_STATIC_GENERATION_ASNYC_STORAGE_LOCATIONS = [ + // Original location of StaticGenerationAsyncStorage + // https://github.com/vercel/next.js/blob/46151dd68b417e7850146d00354f89930d10b43b/packages/next/src/client/components/static-generation-async-storage.ts + 'next/dist/client/components/static-generation-async-storage.js', + // Introduced in Next.js 13.4.20 + // https://github.com/vercel/next.js/blob/e1bc270830f2fc2df3542d4ef4c61b916c802df3/packages/next/src/client/components/static-generation-async-storage.external.ts + 'next/dist/client/components/static-generation-async-storage.external.js', +]; + // TODO: merge default SentryWebpackPlugin ignore with their SentryWebpackPlugin ignore or ignoreFile // TODO: merge default SentryWebpackPlugin include with their SentryWebpackPlugin include // TODO: drop merged keys from override check? `includeDefaults` option? @@ -136,7 +154,13 @@ export function constructWebpackConfigFunction( pageExtensionRegex, excludeServerRoutes: userSentryOptions.excludeServerRoutes, sentryConfigFilePath: getUserConfigFilePath(projectDir, runtime), - nextjsRequestAsyncStorageModulePath: getRequestAsyncStorageModuleLocation( + nextjsRequestAsyncStorageModulePath: getNextjsModuleLocation( + POTENTIAL_REQUEST_ASNYC_STORAGE_LOCATIONS, + projectDir, + rawNewConfig.resolve?.modules, + ), + nextjsStaticGenerationAsyncStorageModulePath: getNextjsModuleLocation( + POTENTIAL_STATIC_GENERATION_ASNYC_STORAGE_LOCATIONS, projectDir, rawNewConfig.resolve?.modules, ), @@ -1016,16 +1040,8 @@ function resolveNextPackageDirFromDirectory(basedir: string): string | undefined } } -const POTENTIAL_REQUEST_ASNYC_STORAGE_LOCATIONS = [ - // Original location of RequestAsyncStorage - // https://github.com/vercel/next.js/blob/46151dd68b417e7850146d00354f89930d10b43b/packages/next/src/client/components/request-async-storage.ts - 'next/dist/client/components/request-async-storage.js', - // Introduced in Next.js 13.4.20 - // https://github.com/vercel/next.js/blob/e1bc270830f2fc2df3542d4ef4c61b916c802df3/packages/next/src/client/components/request-async-storage.external.ts - 'next/dist/client/components/request-async-storage.external.js', -]; - -function getRequestAsyncStorageModuleLocation( +function getNextjsModuleLocation( + potentialLocations: string[], webpackContextDir: string, webpackResolvableModuleLocations: string[] | undefined, ): string | undefined { @@ -1040,7 +1056,7 @@ function getRequestAsyncStorageModuleLocation( for (const webpackResolvableLocation of absoluteWebpackResolvableModuleLocations) { const nextPackageDir = resolveNextPackageDirFromDirectory(webpackResolvableLocation); if (nextPackageDir) { - const asyncLocalStorageLocation = POTENTIAL_REQUEST_ASNYC_STORAGE_LOCATIONS.find(loc => + const asyncLocalStorageLocation = potentialLocations.find(loc => fs.existsSync(path.join(nextPackageDir, '..', loc)), ); if (asyncLocalStorageLocation) { From bd3c74093410d03f1d63afb92b8a1f52a83e783b Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 4 Dec 2023 16:04:40 +0000 Subject: [PATCH 2/3] . --- packages/nextjs/rollup.npm.config.js | 1 - packages/nextjs/src/common/types.ts | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/nextjs/rollup.npm.config.js b/packages/nextjs/rollup.npm.config.js index cc5c477cd727..9c627ea0acbd 100644 --- a/packages/nextjs/rollup.npm.config.js +++ b/packages/nextjs/rollup.npm.config.js @@ -49,7 +49,6 @@ export default [ }, external: [ '@sentry/nextjs', - 'next/dist/client/components/request-async-storage', '__SENTRY_CONFIG_IMPORT_PATH__', '__SENTRY_WRAPPING_TARGET_FILE__', '__SENTRY_NEXTJS_REQUEST_ASYNC_STORAGE_SHIM__', diff --git a/packages/nextjs/src/common/types.ts b/packages/nextjs/src/common/types.ts index fbfd6f78cec1..876c1554b9e3 100644 --- a/packages/nextjs/src/common/types.ts +++ b/packages/nextjs/src/common/types.ts @@ -19,6 +19,10 @@ export type ServerComponentContext = { }; export interface RouteHandlerContext { + // TODO(v8): Remove + /** + * @deprecated The SDK will automatically pick up the method from the incoming Request object instead. + */ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'; parameterizedRoute: string; // TODO(v8): Remove From 7c6f29e0529603a3fbc49db0166f200000275361 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 5 Dec 2023 12:33:29 +0000 Subject: [PATCH 3/3] . --- .../nextjs/src/common/utils/hasStaticBehaviour.ts | 14 -------------- .../src/common/wrapRouteHandlerWithSentry.ts | 11 ++++++----- .../templates/routeHandlerWrapperTemplate.ts | 12 +++++++++++- .../templates/serverComponentWrapperTemplate.ts | 12 +++++++++++- 4 files changed, 28 insertions(+), 21 deletions(-) delete mode 100644 packages/nextjs/src/common/utils/hasStaticBehaviour.ts diff --git a/packages/nextjs/src/common/utils/hasStaticBehaviour.ts b/packages/nextjs/src/common/utils/hasStaticBehaviour.ts deleted file mode 100644 index 4df3aa8cc714..000000000000 --- a/packages/nextjs/src/common/utils/hasStaticBehaviour.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { StaticGenerationStore } from '../types'; - -/** - * Takes a Next.js StaticGenerationStore and determines whether the current state of it would lead to static component/page behaviour. - */ -export function storeHasStaticBehaviour(staticGenerationStore: StaticGenerationStore): boolean { - return !!( - staticGenerationStore?.forceStatic || - staticGenerationStore?.isStaticGeneration || - staticGenerationStore?.dynamicShouldError || - staticGenerationStore?.experimental?.ppr || - staticGenerationStore?.ppr - ); -} diff --git a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts index 9eee5b9beddf..c2fb03bb92fb 100644 --- a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts +++ b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts @@ -23,14 +23,15 @@ export function wrapRouteHandlerWithSentry any>( const currentScope = hub.getScope(); let req: Request | undefined; - // We are not allowed to access the Request object when a route has static behavious. Otherwise Next.js will throw. - if (args[0] instanceof Request && !hasStaticBehaviour) { + let reqMethod: string | undefined; + if (args[0] instanceof Request) { req = args[0]; + reqMethod = req.method; } const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( - sentryTraceHeader ?? req?.headers.get('sentry-trace') ?? undefined, - baggageHeader ?? req?.headers.get('baggage') ?? undefined, + hasStaticBehaviour ? undefined : sentryTraceHeader, + hasStaticBehaviour ? undefined : baggageHeader, ); currentScope.setPropagationContext(propagationContext); @@ -39,7 +40,7 @@ export function wrapRouteHandlerWithSentry any>( res = await trace( { op: 'http.server', - name: `${method} ${parameterizedRoute}`, + name: `${reqMethod ?? method} ${parameterizedRoute}`, status: 'ok', ...traceparentData, metadata: { diff --git a/packages/nextjs/src/config/templates/routeHandlerWrapperTemplate.ts b/packages/nextjs/src/config/templates/routeHandlerWrapperTemplate.ts index ab48f106a890..943b45d1d506 100644 --- a/packages/nextjs/src/config/templates/routeHandlerWrapperTemplate.ts +++ b/packages/nextjs/src/config/templates/routeHandlerWrapperTemplate.ts @@ -7,7 +7,7 @@ import { requestAsyncStorage } from '__SENTRY_NEXTJS_REQUEST_ASYNC_STORAGE_SHIM_ import { staticGenerationAsyncStorage } from '__SENTRY_NEXTJS_STATIC_GENERATION_ASYNC_STORAGE_SHIM__'; // @ts-expect-error See above import * as routeModule from '__SENTRY_WRAPPING_TARGET_FILE__'; -import { storeHasStaticBehaviour } from '../../common/utils/hasStaticBehaviour'; +import type { StaticGenerationStore } from '../../common/types'; import type { RequestAsyncStorage } from './requestAsyncStorageShim'; import type { StaticGenerationAsyncStorage } from './staticGenerationAsyncStorageShim'; @@ -25,6 +25,16 @@ declare const routeModule: { OPTIONS?: (...args: unknown[]) => unknown; }; +function storeHasStaticBehaviour(staticGenerationStore: StaticGenerationStore): boolean { + return !!( + staticGenerationStore?.forceStatic || + staticGenerationStore?.isStaticGeneration || + staticGenerationStore?.dynamicShouldError || + staticGenerationStore?.experimental?.ppr || + staticGenerationStore?.ppr + ); +} + function wrapHandler(handler: T, method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'): T { // Running the instrumentation code during the build phase will mark any function as "dynamic" because we're accessing // the Request object. We do not want to turn handlers dynamic so we skip instrumentation in the build phase. diff --git a/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts b/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts index ca40869d1e8d..33278c3a0633 100644 --- a/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts +++ b/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts @@ -12,7 +12,7 @@ import type { RequestAsyncStorage } from './requestAsyncStorageShim'; // API) we use a shim if it doesn't exist. The logic for this is in the wrapping loader. import { staticGenerationAsyncStorage } from '__SENTRY_NEXTJS_STATIC_GENERATION_ASYNC_STORAGE_SHIM__'; -import { storeHasStaticBehaviour } from '../../common/utils/hasStaticBehaviour'; +import type { StaticGenerationStore } from '../../common/types'; import type { StaticGenerationAsyncStorage } from './staticGenerationAsyncStorageShim'; declare const staticGenerationAsyncStorage: StaticGenerationAsyncStorage; @@ -23,6 +23,16 @@ declare const serverComponentModule: { default: unknown; }; +function storeHasStaticBehaviour(staticGenerationStore: StaticGenerationStore): boolean { + return !!( + staticGenerationStore?.forceStatic || + staticGenerationStore?.isStaticGeneration || + staticGenerationStore?.dynamicShouldError || + staticGenerationStore?.experimental?.ppr || + staticGenerationStore?.ppr + ); +} + const serverComponent = serverComponentModule.default; let wrappedServerComponent;