|
1 | | -import { |
2 | | - GLOBAL_OBJ, |
3 | | - flush, |
4 | | - getDefaultIsolationScope, |
5 | | - getIsolationScope, |
6 | | - logger, |
7 | | - vercelWaitUntil, |
8 | | - withIsolationScope, |
9 | | -} from '@sentry/core'; |
10 | | -import * as SentryNode from '@sentry/node'; |
11 | | -import { type EventHandler, H3Error } from 'h3'; |
| 1 | +import { getDefaultIsolationScope, getIsolationScope, logger, withIsolationScope } from '@sentry/core'; |
| 2 | +import { type EventHandler } from 'h3'; |
12 | 3 | import { defineNitroPlugin } from 'nitropack/runtime'; |
13 | 4 | import type { NuxtRenderHTMLContext } from 'nuxt/app'; |
14 | | -import { addSentryTracingMetaTags, extractErrorContext } from '../utils'; |
| 5 | +import { addSentryTracingMetaTags, flushIfServerless } from '../utils'; |
| 6 | +import { sentryCaptureErrorHook } from '../hooks/captureErrorHook'; |
15 | 7 |
|
16 | 8 | export default defineNitroPlugin(nitroApp => { |
17 | 9 | nitroApp.h3App.handler = patchEventHandler(nitroApp.h3App.handler); |
18 | 10 |
|
19 | | - nitroApp.hooks.hook('error', async (error, errorContext) => { |
20 | | - const sentryClient = SentryNode.getClient(); |
21 | | - const sentryClientOptions = sentryClient?.getOptions(); |
22 | | - |
23 | | - if ( |
24 | | - sentryClientOptions && |
25 | | - 'enableNitroErrorHandler' in sentryClientOptions && |
26 | | - sentryClientOptions.enableNitroErrorHandler === false |
27 | | - ) { |
28 | | - return; |
29 | | - } |
30 | | - |
31 | | - // Do not handle 404 and 422 |
32 | | - if (error instanceof H3Error) { |
33 | | - // Do not report if status code is 3xx or 4xx |
34 | | - if (error.statusCode >= 300 && error.statusCode < 500) { |
35 | | - return; |
36 | | - } |
37 | | - } |
38 | | - |
39 | | - const { method, path } = { |
40 | | - method: errorContext.event?._method ? errorContext.event._method : '', |
41 | | - path: errorContext.event?._path ? errorContext.event._path : null, |
42 | | - }; |
43 | | - |
44 | | - if (path) { |
45 | | - SentryNode.getCurrentScope().setTransactionName(`${method} ${path}`); |
46 | | - } |
47 | | - |
48 | | - const structuredContext = extractErrorContext(errorContext); |
49 | | - |
50 | | - SentryNode.captureException(error, { |
51 | | - captureContext: { contexts: { nuxt: structuredContext } }, |
52 | | - mechanism: { handled: false }, |
53 | | - }); |
54 | | - |
55 | | - await flushIfServerless(); |
56 | | - }); |
| 11 | + nitroApp.hooks.hook('error', sentryCaptureErrorHook); |
57 | 12 |
|
58 | 13 | // @ts-expect-error - 'render:html' is a valid hook name in the Nuxt context |
59 | 14 | nitroApp.hooks.hook('render:html', (html: NuxtRenderHTMLContext) => { |
60 | 15 | addSentryTracingMetaTags(html.head); |
61 | 16 | }); |
62 | 17 | }); |
63 | 18 |
|
64 | | -async function flushIfServerless(): Promise<void> { |
65 | | - const isServerless = |
66 | | - !!process.env.FUNCTIONS_WORKER_RUNTIME || // Azure Functions |
67 | | - !!process.env.LAMBDA_TASK_ROOT || // AWS Lambda |
68 | | - !!process.env.VERCEL || |
69 | | - !!process.env.NETLIFY; |
70 | | - |
71 | | - // @ts-expect-error This is not typed |
72 | | - if (GLOBAL_OBJ[Symbol.for('@vercel/request-context')]) { |
73 | | - vercelWaitUntil(flushWithTimeout()); |
74 | | - } else if (isServerless) { |
75 | | - await flushWithTimeout(); |
76 | | - } |
77 | | -} |
78 | | - |
79 | | -async function flushWithTimeout(): Promise<void> { |
80 | | - const sentryClient = SentryNode.getClient(); |
81 | | - const isDebug = sentryClient ? sentryClient.getOptions().debug : false; |
82 | | - |
83 | | - try { |
84 | | - isDebug && logger.log('Flushing events...'); |
85 | | - await flush(2000); |
86 | | - isDebug && logger.log('Done flushing events'); |
87 | | - } catch (e) { |
88 | | - isDebug && logger.log('Error while flushing events:\n', e); |
89 | | - } |
90 | | -} |
91 | | - |
92 | 19 | function patchEventHandler(handler: EventHandler): EventHandler { |
93 | 20 | return new Proxy(handler, { |
94 | 21 | async apply(handlerTarget, handlerThisArg, handlerArgs: Parameters<EventHandler>) { |
|
0 commit comments