node-err
Version:
Simplify error handling and logging for Node.
379 lines (271 loc) • 8.32 kB
Markdown
Simplify error handling and logging for Node.
<br>
+ [Overview](
+ [Technical](
+ [Examples](
<br>
**One Error Handler**
With node-err, you always use the same error handler, giving you expected behavior...
> `nodeErr.repeat(err);`
```
const nodeErr = require('node-err');
return fetch()
.catch(err => {
return nodeErr.repeat(err);
});
```
<br>
**Bubbling-up Errors**
With consistent error handling, bubbling-up errors is super easy...
```
return fetch()
.catch(err => {
return nodeErr.repeat(err);
})
.catch(err => {
return nodeErr.repeat(err);
})
.catch(err => {
return nodeErr.repeat(err);
})
.catch(err => {
return next(err);
});
```
<br>
**Automatic logging**
And logging/reporting is a breeze, it happens automatically...
> `nodeErr.repeat(err, details);`
```
return fetchUsers()
.catch(err => {
let details = {
name: 'FETCH_USERS',
status: 500,
};
return nodeErr.repeat(err, details);
});
```
<br>
**Server Logging**
Server logging happens out-of-the-box so you can set-up alerts with a service such as Papertrail...
> `papertrail -f SERVER_ERROR`

<br>
<br>
**Custom Logging**
But you can set up your only logger too...
> `nodeErr.setup({ logger: myOwnLogger });`
<br>
**Custom Output Responses**
Configure and send response data for outputting to the browser. View the Simple Promises example to see it in action.
```
nodeErr.setup({ responses: ['user_message','internal_code'] });
return saveAnalytics()
.catch(err => {
return nodeErr.repeat(err, {
status: 500,
responses: {
user_message: 'Oops! Something went wrong.',
internal_code: '2352',
}
});
})
.catch(err => {
let statusCode = nodeErr.getStatus(err);
let outputResponse = nodeErr.getResponse(err);
return res.status(statusCode).send(outputResponse);
});
}
```
<br>
**Localized/Silent Error Handling**
Sometimes you need to handle some errors a little differently, while still allowing other errors to bubble-up or pass through...
> `nodeErr.repeat(customVal)(err);`
```
return saveAnalytics()
.then(() => {
// do more stuff with the ability to
// bubble up a different fatal error
return true;
})
.catch(err => {
// if our saveAnalytics() failed, lets
// keep going and just return false
return nodeErr.repeat(false)(err);
})
.then(result => {
// did the analytics save ok?
return (result) ? next() : retry();
});
}
```
For more details, view the [handling](
<br>
**Async/Await**
```
(async function() {
try {
throw new Error('cubitum irem');
} catch(err) {
await nodeErr.repeat(err, { name: 'Do I know latin?' });
}
})().catch(err => {
// Hey Alexa...
});
```
<br>
**Express Middleware**
How about some Express middleware to catch/report any un-reported errors?
> `nodeErr.stop(err);`
```
app.use((err, req, res, next) => {
let details = { req };
nodeErr.stop(err, details);
return next(err);
});
```
<br>
**Error Output**
What should you do with all of these errors? Whatever you like...
> `app.use((err, req, res, next) => res.sendStatus( nodeErr.getStatus(err) ));`
<br>
**Getting Started**
Just require `node-err` and start using `repeat` to automatically log and bubble-up errors...
```
const nodeErr = require('node-err');
return fetch()
.catch(err => {
return nodeErr.repeat(err);
});
```
<br>
**Config Vars (optional)**
Call the `setup` function in your entry file (index.js or server.js) and pass config vars (optional)...
```
const nodeErr = require('node-err');
nodeErr.setup({
prefix: 'FIND_THIS_ERROR',
status: 401,
debug: true,
logger: (err) => slackChannel('Doh', err)),
responses: ['user_message'],
overrideResponses: true,
});
```
<br>
Key | Type | Description
--- | --- | ---
`prefix` | *string* | Global prefix added to all error logs.
`status` | *number* | Default HTTP status code error.
`debug` | *bool* | Output error repetition tracing.
`logger` | *func* | Custom logging function (accepts Error obj).
`responses` | *array* | Array of response properties.
`overrideResponses` | *bool* | Allow automatic overwrite of responses when bubbling up.
<br>
**Additional Error Details (optional)**
When calling `repeat` add some more details so you know what went wrong (optional)...
```
nodeErr.repeat(err, {
name: 'AWS_IMAGE_NOT_SAVED',
req: req,
status: 400,
context: { imageName: 'budget-report.ppt'},
responses: { user_message: 'Oops! Something went wrong.' },
log: false,
censor: true,
});
```
<br>
Key | Type | Description
--- | --- | ---
`name` | *string* | (optional) Custom error name.
`req` | *object* | (optional) Express request object.
`status` | *bool* | (optional) Desired http status response code.
`context` | *any* | (optional) Whatever works.
`responses` | *object* | (optional) Data to place on the response output.
`log` | *bool* | (optional) Skip logging at this repeat node (intentional errors).
`censor` | *bool* | (optional) Skip request body log (persists with bubbling up).
<br>
If `req` is provided, you'll also get access to:
+ IP Address
+ Requested URL
+ Request Body
+ Request Method
+ User Agent
Any `responses` data provided, must have its property added to the `setup` config.
<br>
**Basic Block Error Handling/Bubbling**<a name="handling"></a>
By default, calling `repeat` on an error the first time will create an error report and then reject it again with a `Promise.reject`.
Calling `repeat` again (in subsequent catch block) on an already reported error will just reject the same error again (no further reporting).
```
return fetch()
.then(() => Promise.reject('level 1 error please'))
.catch(err => {
return nodeErr.repeat(err, { name: 'level 1' }); // report and rejects again
})
.then(() => doSomething()
.catch(err => {
return nodeErr.repeat(err, { name: 'level 2' }); // rejects level 1 error again
})
.then(() => doSomething()
.catch(err => {
return nodeErr.repeat(err, { name: 'level 3' }); // rejects level 1 error again
})
.catch(err => {
return next(err); // returns level 1 error
});
```
In this way, errors can bubble back up and be handled however you like.
<br>
**Multi-directional Error Handling**
You might want to handle some error differently, while still allowing processes above it to pass/bubble their errors through.
By using `repeat` with a custom value, which returns a callback and accepts, you can achieve multi-directional functionality:
+ Any previously reported will continue to bubble up and through.
+ Any un-reported errors will output with whatever value you set.
In this way, you can have a silent error handler for one process without breaking promise chain for processes above/before it.
<br>
> `nodeErr.repeat('It failed!')(err);`
```
return saveAnalytics()
.then(() => {
return fetchUser();
})
.catch(err => {
// if we didnt have a db conn error, and instead had an error
// from saveAnalytics, the below would just return `false`
return nodeErr.repeat(false)(err); // our db conn error will pass through
})
.then(result => {
return (result) ? next() : retry(); // this is skipped because of our db conn error
})
.catch(err => {
return next(err); // return db conn error
})
}
function fetchUser() {
return fetch()
.then(result => {
throw new Error('remote db conn err'); // fire error
})
.then(() => true) // this is skipped
.catch(err => {
return nodeErr.repeat(err, { name: 'FETCH_USER' }); // this will reject again
})
}
```
<br>
You can also force an error report on a silent/custom error, so it will still show in the logs:
> `nodeErr.repeat('It failed!', true)(err);`
<br>
**Viewing the Examples**
There are several examples included in the project. To run these...
1. `$ cd examples`
2. `$ npm install`
3. `$ npm run start`
4. Navigate to `http://localhost:5000` for a directory of examples.