-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathsentry.server.ts
113 lines (96 loc) · 3.41 KB
/
sentry.server.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import {
GLOBAL_OBJ,
flush,
getDefaultIsolationScope,
getIsolationScope,
logger,
vercelWaitUntil,
withIsolationScope,
} from '@sentry/core';
import * as SentryNode from '@sentry/node';
import { type EventHandler, H3Error } from 'h3';
import { defineNitroPlugin } from 'nitropack/runtime';
import type { NuxtRenderHTMLContext } from 'nuxt/app';
import { addSentryTracingMetaTags, extractErrorContext } from '../utils';
export default defineNitroPlugin(nitroApp => {
nitroApp.h3App.handler = patchEventHandler(nitroApp.h3App.handler);
nitroApp.hooks.hook('error', async (error, errorContext) => {
const sentryClient = SentryNode.getClient();
const sentryClientOptions = sentryClient?.getOptions();
if (
sentryClientOptions &&
'enableNitroErrorHandler' in sentryClientOptions &&
sentryClientOptions.enableNitroErrorHandler === false
) {
return;
}
// Do not handle 404 and 422
if (error instanceof H3Error) {
// Do not report if status code is 3xx or 4xx
if (error.statusCode >= 300 && error.statusCode < 500) {
return;
}
}
const { method, path } = {
method: errorContext.event?._method ? errorContext.event._method : '',
path: errorContext.event?._path ? errorContext.event._path : null,
};
if (path) {
SentryNode.getCurrentScope().setTransactionName(`${method} ${path}`);
}
const structuredContext = extractErrorContext(errorContext);
SentryNode.captureException(error, {
captureContext: { contexts: { nuxt: structuredContext } },
mechanism: { handled: false },
});
await flushIfServerless();
});
// @ts-expect-error - 'render:html' is a valid hook name in the Nuxt context
nitroApp.hooks.hook('render:html', (html: NuxtRenderHTMLContext) => {
addSentryTracingMetaTags(html.head);
});
});
async function flushIfServerless(): Promise<void> {
const isServerless =
!!process.env.FUNCTIONS_WORKER_RUNTIME || // Azure Functions
!!process.env.LAMBDA_TASK_ROOT || // AWS Lambda
!!process.env.VERCEL ||
!!process.env.NETLIFY;
// @ts-expect-error This is not typed
if (GLOBAL_OBJ[Symbol.for('@vercel/request-context')]) {
vercelWaitUntil(flushWithTimeout());
} else if (isServerless) {
await flushWithTimeout();
}
}
async function flushWithTimeout(): Promise<void> {
const sentryClient = SentryNode.getClient();
const isDebug = sentryClient ? sentryClient.getOptions().debug : false;
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);
}
}
function patchEventHandler(handler: EventHandler): EventHandler {
return new Proxy(handler, {
async apply(handlerTarget, handlerThisArg, handlerArgs: Parameters<EventHandler>) {
const isolationScope = getIsolationScope();
const newIsolationScope = isolationScope === getDefaultIsolationScope() ? isolationScope.clone() : isolationScope;
logger.log(
`Patched h3 event handler. ${
isolationScope === newIsolationScope ? 'Using existing' : 'Created new'
} isolation scope.`,
);
return withIsolationScope(newIsolationScope, async () => {
try {
return await handlerTarget.apply(handlerThisArg, handlerArgs);
} finally {
await flushIfServerless();
}
});
},
});
}