From 9039128251189fa2d4d6834ed7a48c1ad5ea54a7 Mon Sep 17 00:00:00 2001 From: Gerhard Stoebich <18708370+Flarna@users.noreply.github.com> Date: Wed, 8 Jan 2020 10:22:00 +0100 Subject: [PATCH 01/10] 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. To avoid conflicts with other events it exports a symbol on process which owns this special meaning. --- doc/api/process.md | 28 +++++++++++++++ lib/internal/bootstrap/node.js | 9 ++++- lib/internal/process/execution.js | 7 +++- ...test-process-uncaught-exception-monitor.js | 35 +++++++++++++++++++ 4 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-process-uncaught-exception-monitor.js diff --git a/doc/api/process.md b/doc/api/process.md index a8ca1a641e85e1..60c94ceeff5fcb 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -262,6 +262,20 @@ nonexistentFunc(); console.log('This will not run.'); ``` +It is possible to monitor `'uncaughtException'` events without overriding the +default behavior to exit the process by installing a listener using the symbol +`uncaughtExceptionMonitor`. + +```js +process.on(process.uncaughtExceptionMonitor, (err, origin) => { + MyMonitoringTool.logSync(err, origin); +}); + +// Intentionally cause an exception, but don't catch it. +nonexistentFunc(); +// Still crashes Node.js +``` + #### Warning: Using `'uncaughtException'` correctly `'uncaughtException'` is a crude mechanism for exception handling @@ -2320,6 +2334,20 @@ documentation for the [`'warning'` event][process_warning] and the [`emitWarning()` method][process_emit_warning] for more information about this flag's behavior. +## `process.uncaughtExceptionMonitor` + + +This symbol shall be used to install a listener for only monitoring +`'uncaughtException'` events. Listeners installed using this symbol are called +before the regular `'uncaughtException'` listeners and before a hook +installed via [`process.setUncaughtExceptionCaptureCallback()`][]. + +Installing a listener using this symbol does not change the behavior once an +`'uncaughtException'` event is emitted, therefore the process will still crash +if no regular `'uncaughtException'` listener is installed. + ## `process.umask([mask])` + +* `err` {Error} The uncaught exception. +* `origin` {string} Indicates if the exception originates from an unhandled + rejection or from synchronous errors. Can either be `'uncaughtException'` or + `'unhandledRejection'`. + +The `'uncaughtExceptionMonitor'` event is emitted before an +`'uncaughtException'` event is emitted or a hook installed via +[`process.setUncaughtExceptionCaptureCallback()`][] is called. + +Installing a `'uncaughtExceptionMonitor'` listener does not change the behavior +once an `'uncaughtException'` event is emitted, therefore the process will +still crash if no regular `'uncaughtException'` listener is installed. + +```js +process.on('uncaughtExceptionMonitor', (err, origin) => { + MyMonitoringTool.logSync(err, origin); +}); + +// Intentionally cause an exception, but don't catch it. +nonexistentFunc(); +// Still crashes Node.js +``` + ### Event: `'unhandledRejection'` - -This symbol shall be used to install a listener for only monitoring -`'uncaughtException'` events. Listeners installed using this symbol are called -before the regular `'uncaughtException'` listeners and before a hook -installed via [`process.setUncaughtExceptionCaptureCallback()`][]. - -Installing a listener using this symbol does not change the behavior once an -`'uncaughtException'` event is emitted, therefore the process will still crash -if no regular `'uncaughtException'` listener is installed. - ## `process.umask([mask])`