Skip to content

Commit 7fc2bca

Browse files
authored
feat: enable sentry features (#6133)
* feat: enable sentry features * feat: fine-tuned boundaries and filtering * chore: use <anonymous> * chore: add sample rates * feat: error page and boundaries * chore: sample rate and allow urls * chore: attempt to use tunnel * feat: load ondemand * chore: do not provide error details to users * chore: fixed a comment
1 parent 5882ccb commit 7fc2bca

File tree

10 files changed

+134
-28
lines changed

10 files changed

+134
-28
lines changed

app/[locale]/error.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use client';
2+
3+
import type { FC } from 'react';
4+
5+
const ErrorPage: FC<{ error: Error }> = () => (
6+
<div className="container">
7+
<h2>500: Internal Server Error</h2>
8+
<h3>This Page has thrown a non-recoverable Error</h3>
9+
</div>
10+
);
11+
12+
export default ErrorPage;

app/[locale]/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const RootLayout: FC<PropsWithChildren> = ({ children }) => {
2424

2525
return (
2626
<html className={sourceSans.className} dir={langDir} lang={hrefLang}>
27-
<body>
27+
<body suppressHydrationWarning>
2828
<LocaleProvider>
2929
<ThemeProvider>
3030
<BaseLayout>{children}</BaseLayout>

components/withNodeRelease.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,9 @@ export const WithNodeRelease: FC<WithNodeReleaseProps> = ({
1616
[status].flat().includes(release.status)
1717
);
1818

19-
return <Component release={matchingRelease!} />;
19+
if (matchingRelease !== undefined) {
20+
return <Component release={matchingRelease!} />;
21+
}
22+
23+
return null;
2024
};

layouts/BaseLayout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import type { FC, PropsWithChildren } from 'react';
24

35
import Footer from '@/components/Footer';

next-data/generateNodeReleasesJson.mjs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ const generateNodeReleasesJson = async () => {
6363
};
6464
});
6565

66-
console.timeEnd('g');
67-
6866
return writeFile(
6967
jsonFilePath,
7068
JSON.stringify(

next.config.mjs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22

33
import { withSentryConfig } from '@sentry/nextjs';
44
import withNextIntl from 'next-intl/plugin';
5+
import webpack from 'webpack';
56

67
import {
78
BASE_PATH,
89
ENABLE_STATIC_EXPORT,
910
SENTRY_DSN,
1011
SENTRY_ENABLE,
12+
SENTRY_EXTENSIONS,
13+
SENTRY_TUNNEL,
1114
} from './next.constants.mjs';
1215
import { redirects, rewrites } from './next.rewrites.mjs';
1316

@@ -44,15 +47,20 @@ const nextConfig = {
4447
// as we already check it on the CI within each Pull Request
4548
// we also configure ESLint to run its lint checking on all files (next lint)
4649
eslint: { dirs: ['.'], ignoreDuringBuilds: true },
47-
// Next.js WebPack Bundler does not know how to handle `.mjs` files on `node_modules`
48-
// This is not an issue when using TurboPack as it uses SWC and it is ESM-only
49-
// Once Next.js uses Turbopack for their build process we can remove this
50+
// Adds custom WebPack configuration to our Next.hs setup
5051
webpack: function (config) {
52+
// Next.js WebPack Bundler does not know how to handle `.mjs` files on `node_modules`
53+
// This is not an issue when using TurboPack as it uses SWC and it is ESM-only
54+
// Once Next.js uses Turbopack for their build process we can remove this
5155
config.module.rules.push({
5256
test: /\.m?js$/,
5357
type: 'javascript/auto',
5458
resolve: { fullySpecified: false },
5559
});
60+
61+
// Tree-shakes modules from Sentry Bundle
62+
config.plugins.push(new webpack.DefinePlugin(SENTRY_EXTENSIONS));
63+
5664
return config;
5765
},
5866
experimental: {
@@ -92,7 +100,7 @@ const sentryConfig = {
92100
// Upload Next.js or third-party code in addition to our code
93101
widenClientFileUpload: true,
94102
// Attempt to circumvent ad blockers
95-
tunnelRoute: !ENABLE_STATIC_EXPORT && '/monitoring',
103+
tunnelRoute: SENTRY_TUNNEL(),
96104
// Prevent source map comments in built files
97105
hideSourceMaps: false,
98106
// Tree shake Sentry stuff from the bundle

next.constants.mjs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,30 @@ export const SENTRY_DSN =
196196
*/
197197
export const SENTRY_ENABLE =
198198
process.env.NODE_ENV === 'development' || !!VERCEL_ENV;
199+
200+
/**
201+
* This configures the sampling rate for Sentry
202+
*
203+
* @note we always want to capture 100% on preview branches and development mode
204+
*/
205+
export const SENTRY_CAPTURE_RATE =
206+
SENTRY_ENABLE && BASE_URL !== 'https://nodejs.org' ? 1.0 : 0.01;
207+
208+
/**
209+
* Provides the Route for Sentry's Server-Side Tunnel
210+
*/
211+
export const SENTRY_TUNNEL = (components = '') =>
212+
SENTRY_ENABLE ? `/monitoring${components}` : undefined;
213+
214+
/**
215+
* This configures which Sentry features to tree-shake/remove from the Sentry bundle
216+
*
217+
* @see https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/tree-shaking/
218+
*/
219+
export const SENTRY_EXTENSIONS = {
220+
__SENTRY_DEBUG__: false,
221+
__SENTRY_TRACING__: false,
222+
__RRWEB_EXCLUDE_IFRAME__: true,
223+
__RRWEB_EXCLUDE_SHADOW_DOM__: true,
224+
__SENTRY_EXCLUDE_REPLAY_WORKER__: true,
225+
};

sentry.client.config.ts

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,68 @@
1-
import { SENTRY_DSN, SENTRY_ENABLE } from '@/next.constants.mjs';
1+
import {
2+
Dedupe,
3+
Breadcrumbs,
4+
HttpContext,
5+
LinkedErrors,
6+
BrowserClient,
7+
getCurrentHub,
8+
defaultStackParser,
9+
makeFetchTransport,
10+
} from '@sentry/nextjs';
211

3-
// This lazy-loads Sentry on the Browser
4-
import('@sentry/nextjs').then(({ init }) =>
5-
init({
6-
// Only run Sentry on Vercel Environment
7-
enabled: SENTRY_ENABLE,
8-
// Tell Sentry where to send events
9-
dsn: SENTRY_DSN,
10-
// Disable Sentry Tracing as we don't need to have it
11-
// as Vercel already does Web Vitals and Performance Measurement on Client-Side
12-
enableTracing: false,
13-
// We only want to capture errors from _next folder on production
14-
// We don't want to capture errors from preview branches here
15-
allowUrls: ['https://nodejs.org/_next'],
16-
})
17-
);
12+
import {
13+
SENTRY_DSN,
14+
SENTRY_ENABLE,
15+
SENTRY_CAPTURE_RATE,
16+
SENTRY_TUNNEL,
17+
} from '@/next.constants.mjs';
18+
19+
// This creates a custom Sentry Client with minimal integrations
20+
export const sentryClient = new BrowserClient({
21+
// Only run Sentry on Vercel Environment
22+
enabled: SENTRY_ENABLE,
23+
// Provide Sentry's Secret Key
24+
dsn: SENTRY_DSN,
25+
// Sentry's Error Transport Mechanism
26+
transport: makeFetchTransport,
27+
// Sentry's Stack Trace Parser
28+
stackParser: defaultStackParser,
29+
// All supported Integrations by us
30+
integrations: [
31+
new Dedupe(),
32+
new HttpContext(),
33+
new Breadcrumbs(),
34+
new LinkedErrors(),
35+
],
36+
// We only want to allow ingestion from these pre-selected allowed URLs
37+
// Note that the vercel.app prefix is for our Pull Request Branch Previews
38+
allowUrls: ['https://nodejs.org/', /^https:\/\/.+\.vercel\.app/],
39+
// Percentage of events to send to Sentry (1% of them) (for performance metrics)
40+
tracesSampleRate: SENTRY_CAPTURE_RATE,
41+
// Percentage of events to send to Sentry (1% of them) (for session replays)
42+
replaysSessionSampleRate: SENTRY_CAPTURE_RATE,
43+
// Percentage of events to send to Sentry (1% of them) (for session replays when error happens)
44+
replaysOnErrorSampleRate: 1.0,
45+
// Provides a custom Sentry Tunnel Router
46+
// @note these are components of the Sentry DSN string
47+
// @see @sentry/nextjs/esm/client/tunnelRoute.js
48+
tunnel: SENTRY_TUNNEL(`?o=4506191161786368&p=4506191307735040`),
49+
// Adds custom filtering before sending an Event to Sentry
50+
beforeSend: (event, hint) => {
51+
// Attempts to grab the original Exception before any "magic" happens
52+
const exception = hint.originalException as Error;
53+
54+
// We only want to capture Errors that have a Stack Trace and that are not Anonymous Errors
55+
return exception?.stack && !exception.stack.includes('<anonymous>')
56+
? event
57+
: null;
58+
},
59+
});
60+
61+
// Attaches this Browser Client to Sentry
62+
getCurrentHub().bindClient(sentryClient);
63+
64+
// Loads this Dynamically to avoid adding this to the main bundle (initial load)
65+
import('@sentry/nextjs').then(({ Replay, BrowserTracing }) => {
66+
sentryClient.addIntegration(new Replay({ maskAllText: false }));
67+
sentryClient.addIntegration(new BrowserTracing());
68+
});

sentry.edge.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { SENTRY_DSN, SENTRY_ENABLE } from '@/next.constants.mjs';
55
init({
66
// Only run Sentry on Vercel Environment
77
enabled: SENTRY_ENABLE,
8-
// Tell Sentry where to send events
8+
// Provide Sentry's Secret Key
99
dsn: SENTRY_DSN,
1010
// Percentage of events to send to Sentry (1% of them) (for performance metrics)
1111
tracesSampleRate: 0.01,

sentry.server.config.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import { init } from '@sentry/nextjs';
22
import { ProfilingIntegration } from '@sentry/profiling-node';
33

4-
import { SENTRY_DSN, SENTRY_ENABLE } from '@/next.constants.mjs';
4+
import {
5+
SENTRY_DSN,
6+
SENTRY_ENABLE,
7+
SENTRY_CAPTURE_RATE,
8+
} from '@/next.constants.mjs';
59

610
init({
711
// Only run Sentry on Vercel Environment
812
enabled: SENTRY_ENABLE,
9-
// Tell Sentry where to send events
13+
// Provide Sentry's Secret Key
1014
dsn: SENTRY_DSN,
1115
// Percentage of events to send to Sentry (1% of them) (for performance metrics)
12-
tracesSampleRate: 0.01,
16+
tracesSampleRate: SENTRY_CAPTURE_RATE,
1317
// Percentage of events to send to Sentry (all of them) (for profiling metrics)
1418
// This number is relative to tracesSampleRate - so all traces get profiled
1519
profilesSampleRate: 1.0,

0 commit comments

Comments
 (0)