diff --git a/packages/nextjs/src/common/types.ts b/packages/nextjs/src/common/types.ts index 9e74983b078e..c182e80c2d20 100644 --- a/packages/nextjs/src/common/types.ts +++ b/packages/nextjs/src/common/types.ts @@ -5,7 +5,7 @@ import type { RequestAsyncStorage } from '../config/templates/requestAsyncStorag export type ServerComponentContext = { componentRoute: string; - componentType: string; + componentType: 'Page' | 'Layout' | 'Head' | 'Not-found' | 'Loading' | 'Unknown'; headers?: WebFetchHeaders; }; diff --git a/packages/nextjs/src/common/wrapServerComponentWithSentry.ts b/packages/nextjs/src/common/wrapServerComponentWithSentry.ts index 7785cb0df2fa..1234ea448a3d 100644 --- a/packages/nextjs/src/common/wrapServerComponentWithSentry.ts +++ b/packages/nextjs/src/common/wrapServerComponentWithSentry.ts @@ -3,10 +3,10 @@ import { SPAN_STATUS_ERROR, SPAN_STATUS_OK, captureException, - getCurrentScope, handleCallbackErrors, startSpanManual, withIsolationScope, + withScope, } from '@sentry/core'; import { propagationContextFromHeaders, winterCGHeadersToDict } from '@sentry/utils'; @@ -55,47 +55,50 @@ export function wrapServerComponentWithSentry any> const propagationContext = commonObjectToPropagationContext(context.headers, incomingPropagationContext); return withIsolationScope(isolationScope, () => { - isolationScope.setTransactionName(`${componentType} Server Component (${componentRoute})`); - getCurrentScope().setPropagationContext(propagationContext); - return startSpanManual( - { - op: 'function.nextjs', - name: `${componentType} Server Component (${componentRoute})`, - forceTransaction: true, - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs', - }, - }, - span => { - return handleCallbackErrors( - () => originalFunction.apply(thisArg, args), - error => { - if (isNotFoundNavigationError(error)) { - // We don't want to report "not-found"s - span.setStatus({ code: SPAN_STATUS_ERROR, message: 'not_found' }); - } else if (isRedirectNavigationError(error)) { - // We don't want to report redirects - span.setStatus({ code: SPAN_STATUS_OK }); - } else { - span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); - captureException(error, { - mechanism: { - handled: false, - }, - }); - } - }, - () => { - span.end(); + return withScope(scope => { + scope.setTransactionName(`${componentType} Server Component (${componentRoute})`); - // flushQueue should not throw - // eslint-disable-next-line @typescript-eslint/no-floating-promises - flushQueue(); + scope.setPropagationContext(propagationContext); + return startSpanManual( + { + op: 'function.nextjs', + name: `${componentType} Server Component (${componentRoute})`, + forceTransaction: true, + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs', }, - ); - }, - ); + }, + span => { + return handleCallbackErrors( + () => originalFunction.apply(thisArg, args), + error => { + if (isNotFoundNavigationError(error)) { + // We don't want to report "not-found"s + span.setStatus({ code: SPAN_STATUS_ERROR, message: 'not_found' }); + } else if (isRedirectNavigationError(error)) { + // We don't want to report redirects + span.setStatus({ code: SPAN_STATUS_OK }); + } else { + span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); + captureException(error, { + mechanism: { + handled: false, + }, + }); + } + }, + () => { + span.end(); + + // flushQueue should not throw + // eslint-disable-next-line @typescript-eslint/no-floating-promises + flushQueue(); + }, + ); + }, + ); + }); }); }); }, diff --git a/packages/nextjs/src/config/loaders/wrappingLoader.ts b/packages/nextjs/src/config/loaders/wrappingLoader.ts index 8689082de95b..a0d953d8315b 100644 --- a/packages/nextjs/src/config/loaders/wrappingLoader.ts +++ b/packages/nextjs/src/config/loaders/wrappingLoader.ts @@ -6,7 +6,7 @@ import * as chalk from 'chalk'; import type { RollupBuild, RollupError } from 'rollup'; import { rollup } from 'rollup'; -import type { VercelCronsConfig } from '../../common/types'; +import type { ServerComponentContext, VercelCronsConfig } from '../../common/types'; import type { LoaderThis } from './types'; // Just a simple placeholder to make referencing module consistent @@ -185,7 +185,7 @@ export default function wrappingLoader( .match(/\/?([^/]+)\.(?:js|ts|jsx|tsx)$/); if (componentTypeMatch && componentTypeMatch[1]) { - let componentType; + let componentType: ServerComponentContext['componentType']; switch (componentTypeMatch[1]) { case 'page': componentType = 'Page';