diff --git a/packages/nuxt/src/runtime/plugins/sentry.server.ts b/packages/nuxt/src/runtime/plugins/sentry.server.ts index 1159a6d427ff..d8e46ab96d51 100644 --- a/packages/nuxt/src/runtime/plugins/sentry.server.ts +++ b/packages/nuxt/src/runtime/plugins/sentry.server.ts @@ -2,7 +2,7 @@ import * as Sentry from '@sentry/node'; import { H3Error } from 'h3'; import { defineNitroPlugin } from 'nitropack/runtime'; import type { NuxtRenderHTMLContext } from 'nuxt/app'; -import { addSentryTracingMetaTags, extractErrorContext } from '../utils'; +import { addSentryTracingMetaTags, extractErrorContext, vercelWaitUntilAndFlush } from '../utils'; export default defineNitroPlugin(nitroApp => { nitroApp.hooks.hook('error', (error, errorContext) => { @@ -29,10 +29,16 @@ export default defineNitroPlugin(nitroApp => { captureContext: { contexts: { nuxt: structuredContext } }, mechanism: { handled: false }, }); + + vercelWaitUntilAndFlush(); }); // @ts-expect-error - 'render:html' is a valid hook name in the Nuxt context nitroApp.hooks.hook('render:html', (html: NuxtRenderHTMLContext) => { addSentryTracingMetaTags(html.head); }); + + nitroApp.hooks.hook('afterResponse', () => { + vercelWaitUntilAndFlush(); + }); }); diff --git a/packages/nuxt/src/runtime/utils.ts b/packages/nuxt/src/runtime/utils.ts index 7b56a258f708..7b52f7c1951f 100644 --- a/packages/nuxt/src/runtime/utils.ts +++ b/packages/nuxt/src/runtime/utils.ts @@ -1,6 +1,6 @@ -import { captureException, getClient, getTraceMetaTags } from '@sentry/core'; +import { captureException, flush, getClient, getTraceMetaTags } from '@sentry/core'; import type { ClientOptions, Context } from '@sentry/types'; -import { dropUndefinedKeys } from '@sentry/utils'; +import { dropUndefinedKeys, logger, vercelWaitUntil } from '@sentry/utils'; import type { VueOptions } from '@sentry/vue/src/types'; import type { CapturedErrorContext } from 'nitropack'; import type { NuxtRenderHTMLContext } from 'nuxt/app'; @@ -80,3 +80,31 @@ export function reportNuxtError(options: { }); }); } + +/** + * Flushes pending Sentry events with a 2 seconds timeout and in a way that cannot create unhandled promise rejections. + * + */ +export async function flushSafelyWithTimeout(isDebug: boolean): Promise { + try { + isDebug && logger.log('Flushing events...'); + await flush(2000); + isDebug && logger.log('Done flushing events'); + } catch (e) { + isDebug && logger.log('Error while flushing events:\n', e); + } +} + +/** + * Utility function for the Nuxt module runtime as we always have to get the client instance to get + * the `debug` option (we cannot access BUILD_DEBUG in the module runtime). + * + * This function should be called when Nitro ends a request (so Vercel can wait). + */ +export function vercelWaitUntilAndFlush(): void { + const sentryClient = getClient(); + + if (sentryClient) { + vercelWaitUntil(flushSafelyWithTimeout(sentryClient.getOptions().debug || false)); + } +}