Skip to content

Commit 700562d

Browse files
authored
feat(node): Do not exit process by default when other onUncaughtException handlers are registered in onUncaughtExceptionIntegration (#11532)
1 parent e4ec09e commit 700562d

File tree

5 files changed

+24
-30
lines changed

5 files changed

+24
-30
lines changed

MIGRATION.md

+11
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,7 @@ Sentry.init({
11181118
- [Updated behaviour of `extraErrorDataIntegration`](./MIGRATION.md#extraerrordataintegration-changes)
11191119
- [Updated behaviour of `transactionContext` passed to `tracesSampler`](./MIGRATION.md#transactioncontext-no-longer-passed-to-tracessampler)
11201120
- [Updated behaviour of `getClient()`](./MIGRATION.md#getclient-always-returns-a-client)
1121+
- [Updated behaviour of the SDK in combination with `onUncaughtException` handlers in Node.js](./MIGRATION.md#behaviour-in-combination-with-onuncaughtexception-handlers-in-node.js)
11211122
- [Removal of Client-Side health check transaction filters](./MIGRATION.md#removal-of-client-side-health-check-transaction-filters)
11221123
- [Change of Replay default options (`unblock` and `unmask`)](./MIGRATION.md#change-of-replay-default-options-unblock-and-unmask)
11231124
- [Angular Tracing Decorator renaming](./MIGRATION.md#angular-tracing-decorator-renaming)
@@ -1168,6 +1169,16 @@ some attributes may only be set later during the span lifecycle (and thus not be
11681169
Sentry was actually initialized, using `getClient()` will thus not work anymore. Instead, you should use the new
11691170
`Sentry.isInitialized()` utility to check this.
11701171

1172+
#### Behaviour in combination with `onUncaughtException` handlers in Node.js
1173+
1174+
Previously the SDK exited the process by default, even though additional `onUncaughtException` may have been registered,
1175+
that would have prevented the process from exiting. You could opt out of this behaviour by setting the
1176+
`exitEvenIfOtherHandlersAreRegistered: false` in the `onUncaughtExceptionIntegration` options. Up until now the value
1177+
for this option defaulted to `true`.
1178+
1179+
Going forward, the default value for `exitEvenIfOtherHandlersAreRegistered` will be `false`, meaning that the SDK will
1180+
not exit your process when you have registered other `onUncaughtException` handlers.
1181+
11711182
#### Removal of Client-Side health check transaction filters
11721183

11731184
The SDK no longer filters out health check transactions by default. Instead, they are sent to Sentry but still dropped

dev-packages/node-integration-tests/suites/public-api/OnUncaughtException/test.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,14 @@ describe('OnUncaughtException integration', () => {
1515
});
1616
});
1717

18-
test('should close process on uncaught error when additional listeners are registered', done => {
19-
expect.assertions(3);
18+
test('should not close process on uncaught error when additional listeners are registered', done => {
19+
expect.assertions(2);
2020

2121
const testScriptPath = path.resolve(__dirname, 'additional-listener-test-script.js');
2222

2323
childProcess.exec(`node ${testScriptPath}`, { encoding: 'utf8' }, (err, stdout) => {
24-
expect(err).not.toBeNull();
25-
expect(err?.code).toBe(1);
26-
expect(stdout).not.toBe("I'm alive!");
24+
expect(err).toBeNull();
25+
expect(stdout).toBe("I'm alive!");
2726
done();
2827
});
2928
});

packages/nextjs/src/server/index.ts

-4
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@ import { devErrorSymbolicationEventProcessor } from '../common/devErrorSymbolica
88
import { getVercelEnv } from '../common/getVercelEnv';
99
import { isBuild } from '../common/utils/isBuild';
1010
import { distDirRewriteFramesIntegration } from './distDirRewriteFramesIntegration';
11-
import { onUncaughtExceptionIntegration } from './onUncaughtExceptionIntegration';
1211

1312
export * from '@sentry/node';
1413
import type { EventProcessor } from '@sentry/types';
1514
import { requestIsolationScopeIntegration } from './requestIsolationScopeIntegration';
1615

1716
export { captureUnderscoreErrorException } from '../common/_error';
18-
export { onUncaughtExceptionIntegration } from './onUncaughtExceptionIntegration';
1917

2018
const globalWithInjectedValues = GLOBAL_OBJ as typeof GLOBAL_OBJ & {
2119
__rewriteFramesDistDir__?: string;
@@ -75,13 +73,11 @@ export function init(options: NodeOptions): void {
7573
const customDefaultIntegrations = [
7674
...getDefaultIntegrations(options).filter(
7775
integration =>
78-
integration.name !== 'OnUncaughtException' &&
7976
// Next.js comes with its own Node-Fetch instrumentation, so we shouldn't add ours on-top
8077
integration.name !== 'NodeFetch' &&
8178
// Next.js comes with its own Http instrumentation for OTel which lead to double spans for route handler requests
8279
integration.name !== 'Http',
8380
),
84-
onUncaughtExceptionIntegration(),
8581
requestIsolationScopeIntegration(),
8682
];
8783

packages/nextjs/src/server/onUncaughtExceptionIntegration.ts

-5
This file was deleted.

packages/node/src/integrations/onuncaughtexception.ts

+9-16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { captureException, defineIntegration } from '@sentry/core';
22
import { getClient } from '@sentry/core';
3-
import type { IntegrationFn } from '@sentry/types';
43
import { logger } from '@sentry/utils';
54

65
import { DEBUG_BUILD } from '../debug-build';
@@ -13,17 +12,13 @@ type TaggedListener = NodeJS.UncaughtExceptionListener & {
1312
tag?: string;
1413
};
1514

16-
// CAREFUL: Please think twice before updating the way _options looks because the Next.js SDK depends on it in `index.server.ts`
1715
interface OnUncaughtExceptionOptions {
18-
// TODO(v8): Evaluate whether we should switch the default behaviour here.
19-
// Also, we can evaluate using https://nodejs.org/api/process.html#event-uncaughtexceptionmonitor per default, and
20-
// falling back to current behaviour when that's not available.
2116
/**
2217
* Controls if the SDK should register a handler to exit the process on uncaught errors:
2318
* - `true`: The SDK will exit the process on all uncaught errors.
2419
* - `false`: The SDK will only exit the process when there are no other `uncaughtException` handlers attached.
2520
*
26-
* Default: `true`
21+
* Default: `false`
2722
*/
2823
exitEvenIfOtherHandlersAreRegistered: boolean;
2924

@@ -40,24 +35,22 @@ interface OnUncaughtExceptionOptions {
4035

4136
const INTEGRATION_NAME = 'OnUncaughtException';
4237

43-
const _onUncaughtExceptionIntegration = ((options: Partial<OnUncaughtExceptionOptions> = {}) => {
44-
const _options = {
45-
exitEvenIfOtherHandlersAreRegistered: true,
38+
/**
39+
* Add a global exception handler.
40+
*/
41+
export const onUncaughtExceptionIntegration = defineIntegration((options: Partial<OnUncaughtExceptionOptions> = {}) => {
42+
const optionsWithDefaults = {
43+
exitEvenIfOtherHandlersAreRegistered: false,
4644
...options,
4745
};
4846

4947
return {
5048
name: INTEGRATION_NAME,
5149
setup(client: NodeClient) {
52-
global.process.on('uncaughtException', makeErrorHandler(client, _options));
50+
global.process.on('uncaughtException', makeErrorHandler(client, optionsWithDefaults));
5351
},
5452
};
55-
}) satisfies IntegrationFn;
56-
57-
/**
58-
* Add a global exception handler.
59-
*/
60-
export const onUncaughtExceptionIntegration = defineIntegration(_onUncaughtExceptionIntegration);
53+
});
6154

6255
type ErrorHandler = { _errorHandler: boolean } & ((error: Error) => void);
6356

0 commit comments

Comments
 (0)