diff --git a/packages/nuxt/src/server/sdk.ts b/packages/nuxt/src/server/sdk.ts index a6599b4ac088..4924bb10699c 100644 --- a/packages/nuxt/src/server/sdk.ts +++ b/packages/nuxt/src/server/sdk.ts @@ -1,7 +1,7 @@ -import { applySdkMetadata, getGlobalScope } from '@sentry/core'; -import { init as initNode } from '@sentry/node'; +import { applySdkMetadata, flush, getGlobalScope } from '@sentry/core'; +import { httpIntegration, init as initNode } from '@sentry/node'; import type { Client, EventProcessor } from '@sentry/types'; -import { logger } from '@sentry/utils'; +import { logger, vercelWaitUntil } from '@sentry/utils'; import { DEBUG_BUILD } from '../common/debug-build'; import type { SentryNuxtServerOptions } from '../common/types'; @@ -14,6 +14,17 @@ export function init(options: SentryNuxtServerOptions): Client | undefined { const sentryOptions = { ...options, registerEsmLoaderHooks: mergeRegisterEsmLoaderHooks(options), + integrations: [ + 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()); + }, + }, + }), + ...(Array.isArray(options.integrations) ? options.integrations : []), + ], }; applySdkMetadata(sentryOptions, 'nuxt', ['nuxt', 'node']); @@ -64,3 +75,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); + } +}