-
Notifications
You must be signed in to change notification settings - Fork 30.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
process: allow monitoring uncaughtException
Installing an uncaughtException listener has a side effect that process is not aborted. This is quite bad for monitoring/logging tools which tend to be interested in errors but don't want to cause side effects like swallow an exception or change the output on console. There are some workarounds in the wild like monkey patching emit or rethrow in the exception if monitoring tool detects that it is the only listener but this is error prone and risky. This PR allows to install a listener to monitor uncaughtException without the side effect to consider the exception has handled. PR-URL: #31257 Refs: #30932 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
- Loading branch information
1 parent
deff600
commit 79eba6a
Showing
5 changed files
with
123 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
'use strict'; | ||
|
||
// Keep the event loop alive. | ||
setTimeout(() => {}, 1e6); | ||
|
||
process.on('uncaughtExceptionMonitor', (err) => { | ||
console.log(`Monitored: ${err.message}`); | ||
}); | ||
|
||
throw new Error('Shall exit'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
'use strict'; | ||
|
||
// Keep the event loop alive. | ||
setTimeout(() => {}, 1e6); | ||
|
||
process.on('uncaughtExceptionMonitor', (err) => { | ||
console.log(`Monitored: ${err.message}, will throw now`); | ||
missingFunction(); | ||
}); | ||
|
||
throw new Error('Shall exit'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
'use strict'; | ||
|
||
const common = require('../common'); | ||
const assert = require('assert'); | ||
const { execFile } = require('child_process'); | ||
const fixtures = require('../common/fixtures'); | ||
|
||
{ | ||
// Verify exit behavior is unchanged | ||
const fixture = fixtures.path('uncaught-exceptions', 'uncaught-monitor1.js'); | ||
execFile( | ||
process.execPath, | ||
[fixture], | ||
common.mustCall((err, stdout, stderr) => { | ||
assert.strictEqual(err.code, 1); | ||
assert.strictEqual(Object.getPrototypeOf(err).name, 'Error'); | ||
assert.strictEqual(stdout, 'Monitored: Shall exit\n'); | ||
const errLines = stderr.trim().split(/[\r\n]+/); | ||
const errLine = errLines.find((l) => /^Error/.exec(l)); | ||
assert.strictEqual(errLine, 'Error: Shall exit'); | ||
}) | ||
); | ||
} | ||
|
||
{ | ||
// Verify exit behavior is unchanged | ||
const fixture = fixtures.path('uncaught-exceptions', 'uncaught-monitor2.js'); | ||
execFile( | ||
process.execPath, | ||
[fixture], | ||
common.mustCall((err, stdout, stderr) => { | ||
assert.strictEqual(err.code, 7); | ||
assert.strictEqual(Object.getPrototypeOf(err).name, 'Error'); | ||
assert.strictEqual(stdout, 'Monitored: Shall exit, will throw now\n'); | ||
const errLines = stderr.trim().split(/[\r\n]+/); | ||
const errLine = errLines.find((l) => /^ReferenceError/.exec(l)); | ||
assert.strictEqual( | ||
errLine, | ||
'ReferenceError: missingFunction is not defined' | ||
); | ||
}) | ||
); | ||
} | ||
|
||
const theErr = new Error('MyError'); | ||
|
||
process.on( | ||
'uncaughtExceptionMonitor', | ||
common.mustCall((err, origin) => { | ||
assert.strictEqual(err, theErr); | ||
assert.strictEqual(origin, 'uncaughtException'); | ||
}, 2) | ||
); | ||
|
||
process.on('uncaughtException', common.mustCall((err, origin) => { | ||
assert.strictEqual(origin, 'uncaughtException'); | ||
assert.strictEqual(err, theErr); | ||
})); | ||
|
||
process.nextTick(common.mustCall(() => { | ||
// Test with uncaughtExceptionCaptureCallback installed | ||
process.setUncaughtExceptionCaptureCallback(common.mustCall( | ||
(err) => assert.strictEqual(err, theErr)) | ||
); | ||
|
||
throw theErr; | ||
})); | ||
|
||
throw theErr; |