Skip to content

Commit

Permalink
Add more features
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed Oct 15, 2018
1 parent 8768199 commit 7c63039
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 116 deletions.
56 changes: 35 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,46 +19,60 @@ const onProcessError = require('on-process-error')
const undoSetup = onProcessError.setup()
```

When any process errors occur, it will be logged using `console.error()`:
When any process errors occur, it will be logged using `console.error()`.
The message will include detailed information about the error.

- the message will include detailed information about the error
- for `warning`, `console.warn()` will be used instead.
- for `uncaughtException`, [`process.exit(1)` will be called after
`console.error()`](https://nodejs.org/api/process.html#process_warning_using_uncaughtexception_correctly).
For `warning`, `console.warn()` will be used instead.

You can undo everything by firing the function returned by
`onProcessError.setup()` (called `undoSetup` in the example above).

# Example output

TO BE DONE

# Custom handling

You can override the default behavior by passing a custom function instead.
You can override the default behavior by passing a custom function to the
`handle` option.

<!-- eslint-disable no-empty-function, no-unused-vars, node/no-missing-require,
import/no-unresolved, unicorn/filename-case, strict -->
import/no-unresolved, unicorn/filename-case, strict, no-undef -->

```js
const onProcessError = require('on-process-error')

const undoSetup = onProcessError.setup(
({ eventName, promiseState, promiseValue, error, message }) => {},
)
onProcessError.setup({
handle({ eventName, promiseState, promiseValue, error, message }) {},
})
```

This can be useful if you want to use your own logger instead of the console.

The function's argument is an object with the following properties:

- `eventName` `{string}`: can be `uncaughtException`, `unhandledRejection`,
`rejectionHandled`, `multipleResolves` or `warning`
- `error` `{any}` is either:
- value thrown by `uncaughtException`. Usually an `Error` instance, but not
always.
- `Error` instance emitted by `warning`.
[`error.name`, `error.code` and `error.detail`](https://nodejs.org/api/process.html#process_event_warning)
might be defined.
- `promiseState` `{string}`: whether promise was `resolved` or `rejected`.
For `unhandledRejection`, `rejectionHandled` and `multipleResolves`.
- `promiseValue` `{any}`: value resolved/rejected by the promise.
For `unhandledRejection`, `rejectionHandled` and `multipleResolves`.
- `error` `{error}`:
- can be:
- thrown by `uncaughtException`
- emitted by `warning`. [`error.name`, `error.code` and `error.detail`](https://nodejs.org/api/process.html#process_event_warning)
might be defined.
- rejected by `unhandledRejection`, `rejectionHandled` or
`multipleResolves`'s promise (if the promise was rejected).
- if the error is not an `Error` instance (e.g. if it is a string), it will
be normalized to one using `new Error()`.
- `message` `{string}`: detailed message summing up all of the above.

# Exiting on uncaught exceptions

By default, `uncaughtException` will fire `process.exit(1)`. This is the recommended behavior according to the
[Node.js documentation](https://nodejs.org/api/process.html#process_warning_using_uncaughtexception_correctly).

You can disable this by setting the `exitOnExceptions` option to `false`:

<!-- eslint-disable no-empty-function, no-unused-vars, node/no-missing-require,
import/no-unresolved, unicorn/filename-case, strict, no-undef -->

```js
onProcessError.setup({ exitOnExceptions: false })
```
13 changes: 3 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
},
"author": "autoserver founder <founder@autoserver.io> (http://autoserver.io)",
"directories": {},
"dependencies": {},
"dependencies": {
"chalk": "^2.4.1"
},
"devDependencies": {
"eslint": "^5.7.0",
"eslint-config-prettier": "^3.1.0",
Expand Down
7 changes: 0 additions & 7 deletions src/default.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
'use strict'

const { exit } = require('process')

// Default event handler
const defaultHandler = function({ eventName, message }) {
const level = eventName === 'warning' ? 'warn' : 'error'
// eslint-disable-next-line no-restricted-globals, no-console
console[level](message)

// See https://nodejs.org/api/process.html#process_warning_using_uncaughtexception_correctly
if (eventName === 'uncaughtException') {
exit(1)
}
}

module.exports = {
Expand Down
25 changes: 0 additions & 25 deletions src/error.js

This file was deleted.

2 changes: 1 addition & 1 deletion src/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const warning = function(context, error) {
}

const unhandledRejection = function(context, promiseValue, promise) {
handleEvent({ ...context, promise })
handleEvent({ ...context, promise, promiseValue })
}

const rejectionHandled = function(context, promise) {
Expand Down
51 changes: 33 additions & 18 deletions src/handle.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,50 @@
'use strict'

const { getError } = require('./error')
const { exit } = require('process')

const { parsePromise } = require('./promise')
const { getMessage } = require('./message')

// Generic event handler for all events.
const handleEvent = async function({ handlerFunc, eventName, error, promise }) {
const { promiseState, promiseValue } = await parsePromise({ promise })
const errorA = getError({ error, promiseValue })
const handleEvent = async function({
opts: { handlerFunc, exitOnExceptions },
eventName,
error,
promise,
promiseValue,
}) {
const { promiseState, promiseValue: promiseValueA } = await parsePromise({
eventName,
promise,
promiseValue,
})
const message = getMessage({
eventName,
promiseState,
promiseValue,
error: errorA,
promiseValue: promiseValueA,
error,
})

handlerFunc({ eventName, promiseState, promiseValue, error: errorA, message })
handlerFunc({
eventName,
promiseState,
promiseValue: promiseValueA,
error,
message,
})

exitProcess({ eventName, exitOnExceptions })
}

// Retrieve promise's resolved/rejected state and value.
const parsePromise = async function({ promise }) {
// `uncaughtException` and `warning` events do not have `promise`.
if (promise === undefined) {
return {}
// Exit process on `uncaughtException`
// See https://nodejs.org/api/process.html#process_warning_using_uncaughtexception_correctly
// Can be disabled with `opts.exitOnExceptions: false`
const exitProcess = function({ eventName, exitOnExceptions }) {
if (eventName !== 'uncaughtException' || !exitOnExceptions) {
return
}

try {
const promiseValue = await promise
return { promiseState: 'resolved', promiseValue }
} catch (error) {
return { promiseState: 'rejected', promiseValue: error }
}
exit(1)
}

module.exports = {
Expand Down
21 changes: 14 additions & 7 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,27 @@ const { defaultHandler } = require('./default')
const EVENTS = require('./events')

// Add event handling for all process-related errors
const setup = function(handlerFunc = defaultHandler) {
const listeners = addListeners({ handlerFunc })
const setup = function(opts) {
const optsA = { ...DEFAULT_OPTS, ...opts }

const listeners = addListeners({ opts: optsA })
const removeAll = removeListeners.bind(null, listeners)
return removeAll
}

const addListeners = function({ handlerFunc }) {
return Object.entries(EVENTS).map((eventName, eventFunc) =>
addListener({ handlerFunc, eventName, eventFunc }),
const DEFAULT_OPTS = {
handlerFunc: defaultHandler,
exitOnExceptions: true,
}

const addListeners = function({ opts }) {
return Object.entries(EVENTS).map(([eventName, eventFunc]) =>
addListener({ opts, eventName, eventFunc }),
)
}

const addListener = function({ handlerFunc, eventName, eventFunc }) {
const eventListener = eventFunc.bind(null, { handlerFunc, eventName })
const addListener = function({ opts, eventName, eventFunc }) {
const eventListener = eventFunc.bind(null, { opts, eventName })
process.on(eventName, eventListener)

return { eventListener, eventName }
Expand Down
Loading

0 comments on commit 7c63039

Please sign in to comment.