Skip to content

Commit df79871

Browse files
authored
feat(nuxt): Adding experimental_basicServerTracing option to Nuxt module (#13643)
Enabling this option will import the Sentry server config at the top of the server entry file. This can be used when adding the node option `--import` does not work. This however only comes with limited tracing functionality. Example Usage: ```js export default defineNuxtConfig({ sentry: { // ... other options simplifiedDeployment: true }, })
1 parent 1664dc7 commit df79871

File tree

4 files changed

+102
-26
lines changed

4 files changed

+102
-26
lines changed

packages/nuxt/README.md

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,23 @@ functionality related to Nuxt.
3737

3838
**What is partly working:**
3939

40+
- Source Maps
41+
- Connected Tracing (Frontend & Backend)
4042
- Tracing by setting `tracesSampleRate`
4143
- UI (Vue) traces
4244
- HTTP (Node) traces
4345

44-
**What is not yet(!) included:**
45-
46-
- Source Maps
47-
- Nuxt-specific traces and connecting frontend & backend traces
48-
4946
**Known Issues:**
5047

51-
- When adding `sentry.server.config.(ts/js)`, you get this error: "Failed to register ESM hook", but the application
52-
will still work
53-
- When initializing Sentry on the server with `instrument.server.(js|ts)`, you get an `'import-in-the-middle'` error,
54-
and the application won't work
48+
- When adding `sentry.server.config.(ts/js)`, you get an error like this:
49+
"`Failed to register ESM hook (import-in-the-middle/hook.mjs)`". You can add a resolution for `@vercel/nft` to fix
50+
this. This will add the `hook.mjs` file to your build output
51+
([issue here](https://github.com/unjs/nitro/issues/2703)).
52+
```json
53+
"resolutions": {
54+
"@vercel/nft": "^0.27.4"
55+
}
56+
```
5557

5658
## Automatic Setup
5759

@@ -93,35 +95,57 @@ export default defineNuxtConfig({
9395
Add a `sentry.client.config.(js|ts)` file to the root of your project:
9496

9597
```javascript
98+
import { useRuntimeConfig } from '#imports';
9699
import * as Sentry from '@sentry/nuxt';
97100

98101
Sentry.init({
99-
dsn: process.env.SENTRY_DSN,
102+
// If set up, you can use your runtime config here
103+
dsn: useRuntimeConfig().public.sentry.dsn,
100104
});
101105
```
102106

103107
### 4. Server-side setup
104108

105-
Add an `instrument.server.mjs` file to your `public` folder:
109+
Add an `sentry.client.config.(js|ts)` file to the root of your project:
106110

107111
```javascript
108112
import * as Sentry from '@sentry/nuxt';
109113

110114
// Only run `init` when process.env.SENTRY_DSN is available.
111115
if (process.env.SENTRY_DSN) {
112116
Sentry.init({
113-
dsn: process.env.SENTRY_DSN,
117+
dsn: 'your-dsn',
114118
});
115119
}
116120
```
117121

118-
Add an import flag to the `NODE_OPTIONS` of your preview script in the `package.json` file, so the file loads before any
119-
other imports:
122+
The Nuxt runtime config does not work in the Sentry server to technical reasons (it has to be loaded before Nuxt is
123+
loaded). To be able to use `process.env` you either have to add `--env-file=.env` to your node command
124+
125+
```bash
126+
node --env-file=.env --import ./.output/server/sentry.server.config.mjs .output/server/index.mjs
127+
```
128+
129+
or use the `dotenv` package:
130+
131+
```javascript
132+
import dotenv from 'dotenv';
133+
import * as Sentry from '@sentry/nuxt';
134+
135+
dotenv.config();
136+
137+
Sentry.init({
138+
dsn: process.env.SENTRY_DSN,
139+
});
140+
```
141+
142+
Add an import flag to the Node options of your `node` command (not `nuxt preview`), so the file loads before any other
143+
imports (keep in mind the `.mjs` file ending):
120144

121145
```json
122146
{
123147
"scripts": {
124-
"preview": "NODE_OPTIONS='--import ./public/instrument.server.mjs' nuxt preview"
148+
"start": "node --import ./.output/server/sentry.server.config.mjs .output/server/index.mjs"
125149
}
126150
}
127151
```

packages/nuxt/src/common/types.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,16 @@ export type SentryNuxtModuleOptions = {
9999
* Enabling this will give you, for example, logs about source maps.
100100
*/
101101
debug?: boolean;
102+
103+
/**
104+
* Enabling basic server tracing can be used for environments where modifying the node option `--import` is not possible.
105+
* However, enabling this option only supports limited tracing instrumentation. Only http traces will be collected (but no database-specific traces etc.).
106+
*
107+
* If this option is `true`, the Sentry SDK will import the Sentry server config at the top of the server entry file to load the SDK on the server.
108+
*
109+
* **DO NOT** enable this option if you've already added the node option `--import` in your node start script. This would initialize Sentry twice on the server-side and leads to unexpected issues.
110+
*
111+
* @default false
112+
*/
113+
experimental_basicServerTracing?: boolean;
102114
};

packages/nuxt/src/module.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { addPlugin, addPluginTemplate, addServerPlugin, createResolver, defineNuxtModule } from '@nuxt/kit';
22
import type { SentryNuxtModuleOptions } from './common/types';
3-
import { addServerConfigToBuild } from './vite/addServerConfig';
3+
import { addSentryTopImport, addServerConfigToBuild } from './vite/addServerConfig';
44
import { setupSourceMaps } from './vite/sourceMaps';
55
import { findDefaultSdkInitFile } from './vite/utils';
66

@@ -62,15 +62,20 @@ export default defineNuxtModule<ModuleOptions>({
6262
if (clientConfigFile || serverConfigFile) {
6363
setupSourceMaps(moduleOptions, nuxt);
6464
}
65-
if (serverConfigFile && serverConfigFile.includes('.server.config')) {
66-
if (moduleOptions.debug) {
67-
// eslint-disable-next-line no-console
68-
console.log(
69-
`[Sentry] Using your \`${serverConfigFile}\` file for the server-side Sentry configuration. In case you have a \`public/instrument.server\` file, the \`public/instrument.server\` file will be ignored. Make sure the file path in your node \`--import\` option matches the Sentry server config file in your \`.output\` folder and has a \`.mjs\` extension.`,
70-
);
71-
}
7265

66+
if (serverConfigFile && serverConfigFile.includes('.server.config')) {
7367
addServerConfigToBuild(moduleOptions, nuxt, serverConfigFile);
68+
69+
if (moduleOptions.experimental_basicServerTracing) {
70+
addSentryTopImport(moduleOptions, nuxt);
71+
} else {
72+
if (moduleOptions.debug) {
73+
// eslint-disable-next-line no-console
74+
console.log(
75+
`[Sentry] Using your \`${serverConfigFile}\` file for the server-side Sentry configuration. In case you have a \`public/instrument.server\` file, the \`public/instrument.server\` file will be ignored. Make sure the file path in your node \`--import\` option matches the Sentry server config file in your \`.output\` folder and has a \`.mjs\` extension.`,
76+
);
77+
}
78+
}
7479
}
7580
},
7681
});

packages/nuxt/src/vite/addServerConfig.ts

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as fs from 'fs';
2-
import * as path from 'path';
32
import { createResolver } from '@nuxt/kit';
43
import type { Nuxt } from '@nuxt/schema';
54
import type { SentryNuxtModuleOptions } from '../common/types';
@@ -30,8 +29,9 @@ export function addServerConfigToBuild(
3029
* This is necessary because we need to reference this file path in the node --import option.
3130
*/
3231
nuxt.hook('close', async () => {
33-
const source = path.resolve('.nuxt/dist/server/sentry.server.config.mjs');
34-
const destination = path.resolve('.output/server/sentry.server.config.mjs');
32+
const rootDirResolver = createResolver(nuxt.options.rootDir);
33+
const source = rootDirResolver.resolve('.nuxt/dist/server/sentry.server.config.mjs');
34+
const destination = rootDirResolver.resolve('.output/server/sentry.server.config.mjs');
3535

3636
try {
3737
await fs.promises.access(source, fs.constants.F_OK);
@@ -55,3 +55,38 @@ export function addServerConfigToBuild(
5555
});
5656
});
5757
}
58+
59+
/**
60+
* Adds the Sentry server config import at the top of the server entry file to load the SDK on the server.
61+
* This is necessary for environments where modifying the node option `--import` is not possible.
62+
* However, only limited tracing instrumentation is supported when doing this.
63+
*/
64+
export function addSentryTopImport(moduleOptions: SentryNuxtModuleOptions, nuxt: Nuxt): void {
65+
nuxt.hook('close', async () => {
66+
const rootDirResolver = createResolver(nuxt.options.rootDir);
67+
const entryFilePath = rootDirResolver.resolve('.output/server/index.mjs');
68+
69+
try {
70+
fs.readFile(entryFilePath, 'utf8', (err, data) => {
71+
const updatedContent = `import './sentry.server.config.mjs';\n${data}`;
72+
73+
fs.writeFile(entryFilePath, updatedContent, 'utf8', () => {
74+
if (moduleOptions.debug) {
75+
// eslint-disable-next-line no-console
76+
console.log(
77+
`[Sentry] Successfully added the Sentry import to the server entry file "\`${entryFilePath}\`"`,
78+
);
79+
}
80+
});
81+
});
82+
} catch (err) {
83+
if (moduleOptions.debug) {
84+
// eslint-disable-next-line no-console
85+
console.warn(
86+
`[Sentry] An error occurred when trying to add the Sentry import to the server entry file "\`${entryFilePath}\`":`,
87+
err,
88+
);
89+
}
90+
}
91+
});
92+
}

0 commit comments

Comments
 (0)