-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathindex.server.ts
145 lines (119 loc) · 6.2 KB
/
index.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
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
import { Carrier, getHubFromCarrier, getMainCarrier } from '@sentry/hub';
import { RewriteFrames } from '@sentry/integrations';
import { configureScope, getCurrentHub, init as nodeInit, Integrations } from '@sentry/node';
import { hasTracingEnabled } from '@sentry/tracing';
import { EventProcessor } from '@sentry/types';
import { escapeStringForRegex, logger } from '@sentry/utils';
import * as domainModule from 'domain';
import * as path from 'path';
import { isBuild } from './utils/isBuild';
import { buildMetadata } from './utils/metadata';
import { NextjsOptions } from './utils/nextjsOptions';
import { addIntegration } from './utils/userIntegrations';
export * from '@sentry/node';
export { captureUnderscoreErrorException } from './utils/_error';
// Here we want to make sure to only include what doesn't have browser specifics
// because or SSR of next.js we can only use this.
export { ErrorBoundary, showReportDialog, withErrorBoundary } from '@sentry/react';
type GlobalWithDistDir = typeof global & { __rewriteFramesDistDir__: string };
const domain = domainModule as typeof domainModule & { active: (domainModule.Domain & Carrier) | null };
const isVercel = !!process.env.VERCEL;
/** Inits the Sentry NextJS SDK on node. */
export function init(options: NextjsOptions): void {
if (__DEBUG_BUILD__ && options.debug) {
logger.enable();
}
__DEBUG_BUILD__ && logger.log('Initializing SDK...');
if (sdkAlreadyInitialized()) {
__DEBUG_BUILD__ && logger.log('SDK already initialized');
return;
}
buildMetadata(options, ['nextjs', 'node']);
options.environment = options.environment || process.env.NODE_ENV;
addServerIntegrations(options);
// Right now we only capture frontend sessions for Next.js
options.autoSessionTracking = false;
// In an ideal world, this init function would be called before any requests are handled. That way, every domain we
// use to wrap a request would inherit its scope and client from the global hub. In practice, however, handling the
// first request is what causes us to initialize the SDK, as the init code is injected into `_app` and all API route
// handlers, and those are only accessed in the course of handling a request. As a result, we're already in a domain
// when `init` is called. In order to compensate for this and mimic the ideal world scenario, we stash the active
// domain, run `init` as normal, and then restore the domain afterwards, copying over data from the main hub as if we
// really were inheriting.
const activeDomain = domain.active;
domain.active = null;
nodeInit(options);
const filterTransactions: EventProcessor = event => {
return event.type === 'transaction' && event.transaction === '/404' ? null : event;
};
filterTransactions.id = 'NextServer404TransactionFilter';
configureScope(scope => {
scope.setTag('runtime', 'node');
if (isVercel) {
scope.setTag('vercel', true);
}
scope.addEventProcessor(filterTransactions);
});
if (activeDomain) {
const globalHub = getHubFromCarrier(getMainCarrier());
const domainHub = getHubFromCarrier(activeDomain);
// apply the changes made by `nodeInit` to the domain's hub also
domainHub.bindClient(globalHub.getClient());
domainHub.getScope()?.update(globalHub.getScope());
// `scope.update()` doesn’t copy over event processors, so we have to add it manually
domainHub.getScope()?.addEventProcessor(filterTransactions);
// restore the domain hub as the current one
domain.active = activeDomain;
}
__DEBUG_BUILD__ && logger.log('SDK successfully initialized');
}
function sdkAlreadyInitialized(): boolean {
const hub = getCurrentHub();
return !!hub.getClient();
}
function addServerIntegrations(options: NextjsOptions): void {
// 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 = (global as GlobalWithDistDir).__rewriteFramesDistDir__ || '.next';
// nextjs always puts the build directory at the project root level, which is also where you run `next start` from, so
// we can read in the project directory from the currently running process
const distDirAbsPath = path.resolve(process.cwd(), distDirName);
const SOURCEMAP_FILENAME_REGEX = new RegExp(escapeStringForRegex(distDirAbsPath));
const defaultRewriteFramesIntegration = new RewriteFrames({
iteratee: frame => {
frame.filename = frame.filename?.replace(SOURCEMAP_FILENAME_REGEX, 'app:///_next');
return frame;
},
});
if (options.integrations) {
options.integrations = addIntegration(defaultRewriteFramesIntegration, options.integrations);
} else {
options.integrations = [defaultRewriteFramesIntegration];
}
if (hasTracingEnabled(options)) {
const defaultHttpTracingIntegration = new Integrations.Http({ tracing: true });
options.integrations = addIntegration(defaultHttpTracingIntegration, options.integrations, {
Http: { keyPath: '_tracing', value: true },
});
}
}
export type { SentryWebpackPluginOptions } from './config/types';
export { withSentryConfig } from './config';
export { isBuild } from './utils/isBuild';
export { withSentryGetServerSideProps, withSentryGetStaticProps, withSentryGetInitialProps } from './config/wrappers';
export { withSentry } from './utils/withSentry';
// Wrap various server methods to enable error monitoring and tracing. (Note: This only happens for non-Vercel
// deployments, because the current method of doing the wrapping a) crashes Next 12 apps deployed to Vercel and
// b) doesn't work on those apps anyway. We also don't do it during build, because there's no server running in that
// phase.)
if (!isVercel && !isBuild()) {
// Dynamically require the file because even importing from it causes Next 12 to crash on Vercel.
// In environments where the JS file doesn't exist, such as testing, import the TS file.
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { instrumentServer } = require('./utils/instrumentServer.js');
instrumentServer();
} catch (err) {
__DEBUG_BUILD__ && logger.warn(`Error: Unable to instrument server for tracing. Got ${err}.`);
}
}