-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
index.ts
190 lines (157 loc) · 6.54 KB
/
index.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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
import { addEventProcessor, addTracingExtensions, applySdkMetadata, getClient } from '@sentry/core';
import { getDefaultIntegrations, init as nodeInit } from '@sentry/node';
import type { NodeOptions } from '@sentry/node';
import { GLOBAL_OBJ, logger } from '@sentry/utils';
import { DEBUG_BUILD } from '../common/debug-build';
import { devErrorSymbolicationEventProcessor } from '../common/devErrorSymbolicationEventProcessor';
import { getVercelEnv } from '../common/getVercelEnv';
import { isBuild } from '../common/utils/isBuild';
import { distDirRewriteFramesIntegration } from './distDirRewriteFramesIntegration';
import { onUncaughtExceptionIntegration } from './onUncaughtExceptionIntegration';
export * from '@sentry/node';
import type { EventProcessor } from '@sentry/types';
import { promoteHttpSpansIntegration } from './promoteHttpSpansIntegration';
export { captureUnderscoreErrorException } from '../common/_error';
export { onUncaughtExceptionIntegration } from './onUncaughtExceptionIntegration';
const globalWithInjectedValues = GLOBAL_OBJ as typeof GLOBAL_OBJ & {
__rewriteFramesDistDir__?: string;
__sentryRewritesTunnelPath__?: string;
};
/**
* A passthrough error boundary for the server that doesn't depend on any react. Error boundaries don't catch SSR errors
* so they should simply be a passthrough.
*/
export const ErrorBoundary = (props: React.PropsWithChildren<unknown>): React.ReactNode => {
if (!props.children) {
return null;
}
if (typeof props.children === 'function') {
return (props.children as () => React.ReactNode)();
}
// since Next.js >= 10 requires React ^16.6.0 we are allowed to return children like this here
return props.children as React.ReactNode;
};
/**
* A passthrough redux enhancer for the server that doesn't depend on anything from the `@sentry/react` package.
*/
export function createReduxEnhancer() {
return (createStore: unknown) => createStore;
}
/**
* A passthrough error boundary wrapper for the server that doesn't depend on any react. Error boundaries don't catch
* SSR errors so they should simply be a passthrough.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function withErrorBoundary<P extends Record<string, any>>(
WrappedComponent: React.ComponentType<P>,
): React.FC<P> {
return WrappedComponent as React.FC<P>;
}
/**
* Just a passthrough since we're on the server and showing the report dialog on the server doesn't make any sense.
*/
export function showReportDialog(): void {
return;
}
/** Inits the Sentry NextJS SDK on node. */
export function init(options: NodeOptions): void {
addTracingExtensions();
if (isBuild()) {
return;
}
const customDefaultIntegrations = [
...getDefaultIntegrations(options).filter(
integration =>
integration.name !== 'OnUncaughtException' &&
// Next.js comes with its own Node-Fetch instrumentation so we shouldn't add ours on-top
integration.name !== 'NodeFetch' &&
integration.name !== 'Http',
),
promoteHttpSpansIntegration(),
onUncaughtExceptionIntegration(),
];
// This value is injected at build time, based on the output directory specified in the build config. Though a default
// is set there, we set it here as well, just in case something has gone wrong with the injection.
const distDirName = globalWithInjectedValues.__rewriteFramesDistDir__;
if (distDirName) {
customDefaultIntegrations.push(distDirRewriteFramesIntegration({ distDirName }));
}
const opts = {
environment: process.env.SENTRY_ENVIRONMENT || getVercelEnv(false) || process.env.NODE_ENV,
defaultIntegrations: customDefaultIntegrations,
...options,
// Right now we only capture frontend sessions for Next.js
autoSessionTracking: false,
};
if (DEBUG_BUILD && opts.debug) {
logger.enable();
}
DEBUG_BUILD && logger.log('Initializing SDK...');
if (sdkAlreadyInitialized()) {
DEBUG_BUILD && logger.log('SDK already initialized');
return;
}
applySdkMetadata(opts, 'nextjs', ['nextjs', 'node']);
nodeInit(opts);
addEventProcessor(
Object.assign(
(event => {
if (event.type === 'transaction') {
// Filter out transactions for static assets
// This regex matches the default path to the static assets (`_next/static`) and could potentially filter out too many transactions.
// We match `/_next/static/` anywhere in the transaction name because its location may change with the basePath setting.
if (event.transaction?.match(/^GET (\/.*)?\/_next\/static\//)) {
return null;
}
// Filter out transactions for requests to the tunnel route
if (
globalWithInjectedValues.__sentryRewritesTunnelPath__ &&
event.transaction === `POST ${globalWithInjectedValues.__sentryRewritesTunnelPath__}`
) {
return null;
}
// Filter out requests to resolve source maps for stack frames in dev mode
if (event.transaction?.match(/\/__nextjs_original-stack-frame/)) {
return null;
}
// Filter out /404 transactions for pages-router which seem to be created excessively
if (event.transaction === '/404') {
return null;
}
return event;
} else {
return event;
}
}) satisfies EventProcessor,
{ id: 'NextLowQualityTransactionsFilter' },
),
);
addEventProcessor(
Object.assign(
(event => {
if (event.type === 'transaction') {
event.spans = event.spans?.filter(span => {
// Filter out spans for Sentry event sends
const httpTargetAttribute: unknown = span.data?.['http.target'];
if (typeof httpTargetAttribute === 'string') {
// TODO: Find a more robust matching logic - We likely want to use the OTEL SDK's `suppressTracing` in our transport, if we end up using it, we can delete this filtering logic here.
return !httpTargetAttribute.includes('sentry_client') && !httpTargetAttribute.includes('sentry_key');
}
return true;
});
}
return event;
}) satisfies EventProcessor,
{ id: 'NextFilterSentrySpans' },
),
);
if (process.env.NODE_ENV === 'development') {
addEventProcessor(devErrorSymbolicationEventProcessor);
}
DEBUG_BUILD && logger.log('SDK successfully initialized');
}
function sdkAlreadyInitialized(): boolean {
return !!getClient();
}
export * from '../common';
export { wrapApiHandlerWithSentry } from '../common/wrapApiHandlerWithSentry';