Skip to content

feat(nextjs): Remove client.(server|client).config.ts functionality in favor of instrumentation.ts #11059

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ccf4f85
feat(nextjs): Allow omitting `client.(server|client).config.ts` and n…
lforst Mar 12, 2024
68df8ea
.
lforst Mar 12, 2024
d5dacb9
comment
lforst Mar 13, 2024
1f04110
fix unit tests
lforst Mar 13, 2024
2bd96d3
Update withSentryConfig
lforst Mar 13, 2024
b4defba
.
lforst Mar 13, 2024
39eeef8
remove unneeded stuff
lforst Mar 14, 2024
5b8aab0
Remove unnecessary test
lforst Mar 14, 2024
352aea5
feat(nextjs): Bump minimum required Next.js version to `13.2.0`
lforst Mar 14, 2024
2638403
.
lforst Mar 14, 2024
858f7d3
hm
lforst Mar 14, 2024
39a3622
fix
lforst Mar 14, 2024
f7686c2
Merge branch 'develop' into lforst-nextjs-instrumentts
lforst Mar 14, 2024
f55d122
lint
lforst Mar 14, 2024
09d7011
Merge branch 'lforst-min-nextjs-version' into lforst-nextjs-instrumentts
lforst Mar 14, 2024
a8966a6
migrate
lforst Mar 14, 2024
de7a1a9
fix
lforst Mar 14, 2024
ccddc92
Merge branch 'develop' into lforst-min-nextjs-version
lforst Mar 14, 2024
9aad6a4
Merge branch 'develop' into lforst-nextjs-instrumentts
lforst Mar 14, 2024
9a8ed80
.
lforst Mar 14, 2024
6bfbfbb
test
lforst Mar 14, 2024
6a89e31
.
lforst Mar 14, 2024
55103c8
Merge remote-tracking branch 'origin/develop' into lforst-min-nextjs-…
lforst Mar 14, 2024
2e2f00b
Merge branch 'lforst-min-nextjs-version' into lforst-nextjs-instrumentts
lforst Mar 14, 2024
51a5612
Merge remote-tracking branch 'origin/develop' into lforst-nextjs-inst…
lforst Mar 15, 2024
7adf28f
.
lforst Mar 15, 2024
864defc
hell yea
lforst Mar 15, 2024
d7c8669
.
lforst Mar 15, 2024
e48c81d
.
lforst Mar 15, 2024
752580f
fix tests
lforst Mar 18, 2024
0079726
Merge branch 'develop' into lforst-nextjs-instrumentts
lforst Mar 18, 2024
9e4b8f3
undeactivate tests
lforst Mar 18, 2024
f169bea
Update readme
lforst Mar 18, 2024
5e2f44c
Be more clear that sentry.client.config.ts is still supported
lforst Mar 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,56 @@ setup for source maps in Sentry and will not require you to match stack frame pa
To see the new options, check out the docs at https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/,
or look at the TypeScript type definitions of `withSentryConfig`.

#### Updated the recommended way of calling `Sentry.init()`

With version 8 of the SDK we will no longer support the use of `sentry.server.config.ts` and `sentry.edge.config.ts`
files. Instead, please initialize the Sentry Next.js SDK for the serverside in a
[Next.js instrumentation hook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation).
**`sentry.client.config.ts|js` is still supported and encouraged for initializing the clientside SDK.**

The following is an example of how to initialize the serverside SDK in a Next.js instrumentation hook:

1. First, enable the Next.js instrumentation hook by setting the `experimental.instrumentationHook` to `true` in your
`next.config.js`.
2. Next, create a `instrumentation.ts|js` file in the root directory of your project (or in the `src` folder if you have
have one).
3. Now, export a `register` function from the `instrumentation.ts|js` file and call `Sentry.init()` inside of it:

```ts
import * as Sentry from '@sentry/nextjs';

export function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
Sentry.init({
dsn: 'YOUR_DSN',
// Your Node.js Sentry configuration...
});
}

if (process.env.NEXT_RUNTIME === 'edge') {
Sentry.init({
dsn: 'YOUR_DSN',
// Your Edge Runtime Sentry configuration...
});
}
}
```

Note that you can initialize the SDK differently depending on which server runtime is being used.

If you are using a
[Next.js custom server](https://nextjs.org/docs/pages/building-your-application/configuring/custom-server), the
`instrumentation.ts` hook is not called by Next.js so you need to manually call it yourself from within your server
code. It is recommended to do so as early as possible in your application lifecycle.

**Why are we making this change?** The very simple reason is that Next.js requires us to set up OpenTelemetry
instrumentation inside the `register` function of the instrumentation hook. Looking a little bit further into the
future, we also would like the Sentry SDK to be compatible with [Turbopack](https://turbo.build/pack), which is gonna be
the bundler that Next.js will be using instead of Webpack. The SDK in its previous version depended heavily on Webpack
in order to inject the `sentry.(server|edge).config.ts` files into the server-side code. Because this will not be
possible in the future, we are doing ourselves a favor and doing things the way Next.js intends us to do them -
hopefully reducing bugs and jank.

### Astro SDK

#### Removal of `trackHeaders` option for Astro middleware
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as Sentry from '@sentry/nextjs';

declare global {
namespace globalThis {
var transactionIds: string[];
}
}

export function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
Sentry.init({
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 1.0,
integrations: [Sentry.localVariablesIntegration()],
});

Sentry.addEventProcessor(event => {
global.transactionIds = global.transactionIds || [];

if (event.type === 'transaction') {
const eventId = event.event_id;

if (eventId) {
global.transactionIds.push(eventId);
}
}

return event;
});
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as Sentry from '@sentry/nextjs';

export function register() {
if (process.env.NEXT_RUNTIME === 'nodejs' || process.env.NEXT_RUNTIME === 'edge') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This begs a question: Suppose I have the same config for node and edge (as in this example). Do I even need the if statement? Asking because I guess this is a common thing for users and if it's indeed necessary, we should point that out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is more to be in line with what the Next.js docs say and to be forwards compatible. Let's say Next.js also starts running the register hook on the browser, not having the if statement would horribly fail in some cases.

Sentry.init({
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
tunnel: `http://localhost:3031/`, // proxy server
tracesSampleRate: 1.0,
sendDefaultPii: true,
});
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as Sentry from '@sentry/nextjs';

export function register() {
if (process.env.NEXT_RUNTIME === 'nodejs' || process.env.NEXT_RUNTIME === 'edge') {
Sentry.init({
environment: 'qa', // dynamic sampling bias to keep transactions
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
tunnel: `http://localhost:3031/`, // proxy server
tracesSampleRate: 1.0,
sendDefaultPii: true,
});
}
}

This file was deleted.

This file was deleted.

47 changes: 38 additions & 9 deletions packages/nextjs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,64 @@

## Compatibility

Currently, the minimum Next.js supported version is `10.0.8`.
Currently, the minimum Next.js supported version is `11.2.0`.

## General

This package is a wrapper around `@sentry/node` for the server and `@sentry/react` for the client, with added
functionality related to Next.js.

To use this SDK, init it in the Sentry config files.
To use this SDK, initialize it in the Next.js configuration, in the `sentry.client.config.ts|js` file, and in the
[Next.js Instrumentation Hook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)
(`instrumentation.ts|js`).

```javascript
// sentry.client.config.js
// next.config.js

const { withSentryConfig } = require('@sentry/nextjs');

const nextConfig = {
experimental: {
// The instrumentation hook is required for Sentry to work on the serverside
instrumentationHook: true,
},
};

// Wrap the Next.js configuration with Sentry
module.exports = withSentryConfig(nextConfig);
```

```javascript
// sentry.client.config.js or .ts

import * as Sentry from '@sentry/nextjs';

Sentry.init({
dsn: '__DSN__',
// ...
// Your Sentry configuration for the Browser...
});
```

```javascript
// sentry.server.config.js
// instrumentation.ts

import * as Sentry from '@sentry/nextjs';

Sentry.init({
dsn: '__DSN__',
// ...
});
export function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
Sentry.init({
dsn: '__DSN__',
// Your Node.js Sentry configuration...
});
}

if (process.env.NEXT_RUNTIME === 'edge') {
Sentry.init({
dsn: '__DSN__',
// Your Edge Runtime Sentry configuration...
});
}
}
```

To set context information or send manual events, use the exported functions of `@sentry/nextjs`.
Expand Down
2 changes: 2 additions & 0 deletions packages/nextjs/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const config: PlaywrightTestConfig = {
cwd: path.join(__dirname, 'test', 'integration'),
command: 'yarn start',
port: 3000,
stdout: 'pipe',
stderr: 'pipe',
},
};

Expand Down
30 changes: 2 additions & 28 deletions packages/nextjs/src/config/loaders/wrappingLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ const middlewareWrapperTemplateCode = fs.readFileSync(middlewareWrapperTemplateP

let showedMissingAsyncStorageModuleWarning = false;

const sentryInitWrapperTemplatePath = path.resolve(__dirname, '..', 'templates', 'sentryInitWrapperTemplate.js');
const sentryInitWrapperTemplateCode = fs.readFileSync(sentryInitWrapperTemplatePath, { encoding: 'utf8' });

const serverComponentWrapperTemplatePath = path.resolve(
__dirname,
'..',
Expand All @@ -45,8 +42,7 @@ export type WrappingLoaderOptions = {
appDir: string | undefined;
pageExtensionRegex: string;
excludeServerRoutes: Array<RegExp | string>;
wrappingTargetKind: 'page' | 'api-route' | 'middleware' | 'server-component' | 'sentry-init' | 'route-handler';
sentryConfigFilePath?: string;
wrappingTargetKind: 'page' | 'api-route' | 'middleware' | 'server-component' | 'route-handler';
vercelCronsConfig?: VercelCronsConfig;
nextjsRequestAsyncStorageModulePath?: string;
};
Expand All @@ -70,7 +66,6 @@ export default function wrappingLoader(
pageExtensionRegex,
excludeServerRoutes = [],
wrappingTargetKind,
sentryConfigFilePath,
vercelCronsConfig,
nextjsRequestAsyncStorageModulePath,
} = 'getOptions' in this ? this.getOptions() : this.query;
Expand All @@ -79,28 +74,7 @@ export default function wrappingLoader(

let templateCode: string;

if (wrappingTargetKind === 'sentry-init') {
templateCode = sentryInitWrapperTemplateCode;

// Absolute paths to the sentry config do not work with Windows: https://github.com/getsentry/sentry-javascript/issues/8133
// Se we need check whether `this.resourcePath` is absolute because there is no contract by webpack that says it is absolute.
// Examples where `this.resourcePath` could possibly be non-absolute are virtual modules.
if (sentryConfigFilePath && path.isAbsolute(this.resourcePath)) {
const sentryConfigImportPath = path
.relative(path.dirname(this.resourcePath), sentryConfigFilePath)
.replace(/\\/g, '/');

// path.relative() may return something like `sentry.server.config.js` which is not allowed. Imports from the
// current directory need to start with './'.This is why we prepend the path with './', which should always again
// be a valid relative path.
// https://github.com/getsentry/sentry-javascript/issues/8798
templateCode = templateCode.replace(/__SENTRY_CONFIG_IMPORT_PATH__/g, `./${sentryConfigImportPath}`);
} else {
// Bail without doing any wrapping
this.callback(null, userCode, userModuleSourceMap);
return;
}
} else if (wrappingTargetKind === 'page' || wrappingTargetKind === 'api-route') {
if (wrappingTargetKind === 'page' || wrappingTargetKind === 'api-route') {
if (pagesDir === undefined) {
this.callback(null, userCode, userModuleSourceMap);
return;
Expand Down
4 changes: 4 additions & 0 deletions packages/nextjs/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export type NextConfigObject = {
fallback?: NextRewrite[];
}
>;
// Next.js experimental options
experimental?: {
instrumentationHook?: boolean;
};
};

export type SentryBuildOptions = {
Expand Down
Loading