diff --git a/packages/nuxt/src/server/sdk.ts b/packages/nuxt/src/server/sdk.ts index a6599b4ac088..59832bbb2a39 100644 --- a/packages/nuxt/src/server/sdk.ts +++ b/packages/nuxt/src/server/sdk.ts @@ -1,7 +1,12 @@ -import { applySdkMetadata, getGlobalScope } from '@sentry/core'; -import { init as initNode } from '@sentry/node'; -import type { Client, EventProcessor } from '@sentry/types'; -import { logger } from '@sentry/utils'; +import { applySdkMetadata, flush, getGlobalScope } from '@sentry/core'; +import { + type NodeOptions, + getDefaultIntegrations as getDefaultNodeIntegrations, + httpIntegration, + init as initNode, +} from '@sentry/node'; +import type { Client, EventProcessor, Integration } from '@sentry/types'; +import { logger, vercelWaitUntil } from '@sentry/utils'; import { DEBUG_BUILD } from '../common/debug-build'; import type { SentryNuxtServerOptions } from '../common/types'; @@ -14,6 +19,7 @@ export function init(options: SentryNuxtServerOptions): Client | undefined { const sentryOptions = { ...options, registerEsmLoaderHooks: mergeRegisterEsmLoaderHooks(options), + defaultIntegrations: getNuxtDefaultIntegrations(options), }; applySdkMetadata(sentryOptions, 'nuxt', ['nuxt', 'node']); @@ -46,6 +52,21 @@ export function init(options: SentryNuxtServerOptions): Client | undefined { return client; } +function getNuxtDefaultIntegrations(options: NodeOptions): Integration[] { + return [ + ...getDefaultNodeIntegrations(options).filter(integration => integration.name !== 'Http'), + // The httpIntegration is added as defaultIntegration, so users can still overwrite it + httpIntegration({ + instrumentation: { + responseHook: () => { + // Makes it possible to end the tracing span before closing the Vercel lambda (https://vercel.com/docs/functions/functions-api-reference#waituntil) + vercelWaitUntil(flushSafelyWithTimeout()); + }, + }, + }), + ]; +} + /** * Adds /vue/ to the registerEsmLoaderHooks options and merges it with the old values in the array if one is defined. * If the registerEsmLoaderHooks option is already a boolean, nothing is changed. @@ -64,3 +85,16 @@ export function mergeRegisterEsmLoaderHooks( } return options.registerEsmLoaderHooks ?? { exclude: [/vue/] }; } + +/** + * Flushes pending Sentry events with a 2-second timeout and in a way that cannot create unhandled promise rejections. + */ +export async function flushSafelyWithTimeout(): Promise { + try { + DEBUG_BUILD && logger.log('Flushing events...'); + await flush(2000); + DEBUG_BUILD && logger.log('Done flushing events'); + } catch (e) { + DEBUG_BUILD && logger.log('Error while flushing events:\n', e); + } +}