contextualizer

1.0.3 • Public • Published

Contextualizer

Easily wraps errors passed to callbacks to provide more context.

Installation

$ npm install contextualizer

The Problem

When you have an error passed up through various levels of callbacks, you can't tell which function called the lower level function.

The Solution

You need to wrap the error. Contextualizer is a thin wrapper on verror (recommended here: https://www.joyent.com/developers/node/design/errors) that allows you to write this:

var VError = require('verror');
 
function dataRequest(input, callback) {
    databaseLookup(input, function(err, data) {
        if (err) {
            var wrapped_err = new VError(err, 'error in dataRequest')
            return callback(wrapped_err)
        }
        callback(null, data)
    });
}

Like this:

var addContext = require('contextualizer')
 
function dataRequest(input, callback) {
    databaseLookup(input, function(err, data) {
        callback(addContext(err, 'error in dataRequest'), data)
    });
}

Usage

contextualizer(error[, message])

  • error (Error|Function) - The error to wrap. In the event of no error (falsy value), that same falsy value will be returned. Alternatively, you can pass the entire callback function in here and it will be wrapped. See not recommended but better than nothing below.
  • message (string) - An optional message to prepend to error message of the wrapped error. If nothing is passed, the default of [error wrapper] will be used. If the first argument is a function, an error will be thrown if there is no message passed.

Examples

Here's an example of how you might use it in an express app. The first 2 endpoints don't use contextualizer, the second do. Note that I'm assigning the "contextualizer" function to addContext

var addContext = require('contextulizer')
var express = require('express')
var request = require('supertest')
 
 
// This is a function that's called in a lot of different places in our app.
// It'll send an error to the callback if one happens.
function writeToDatabase(data, callback) {
    process.nextTick(function() {
        callback(new Error('worst database ever'))
    })
}
 
 
// Now here's a fake express app that will collect data from various places
var app = express();
 
/*** These endpoints have bad logging ***/
    var badLogRouter = express.Router()
    // Save data from the website
    badLogRouter.post('/web/save', function(req, res, next) {
        writeToDatabase(req.body, function(err) {
            if (err) return next(err)
            res.send('save complete')
        })
    })
    // Save data from the API
    badLogRouter.post('/api/save', function(req, res, next) {
        writeToDatabase(req.body, function(err) {
            if (err) return next(err)
            res.send('save complete')
        })
    })
    app.use('/BadLog', badLogRouter)
 
/*** These endpoints have good logging ***/
    var goodLogRouter = express.Router()
    // Save data from the website
    goodLogRouter.post('/web/save', function(req, res, next) {
        writeToDatabase(req.body, function(err) {
            if (err) return next(addContext(err))
            res.send('save complete')
        })
    })
    // Save data from the API
    goodLogRouter.post('/api/save', function(req, res, next) {
        writeToDatabase(req.body, function(err) {
            var msg = 'error saving from API in good log router'
            if (err) return next(addContext(err, msg))
            res.send('save complete')
        })
    })
    app.use('/GoodLog', goodLogRouter)
 
/*** Here's the error middleware where the errors get logged ***/
app.use(function(err, req, res, next) {
    console.log(err.stack)
    console.log('-----------------------------')
})

Errors returned from the first 2 endpoints look exactly the same in the logs and don't contain any context that can be helpful for troubleshooting.

Error: worst database ever
    at /Users/nigel/about.me/contextualizer/script.js:10:18
    at process._tickCallback (node.js:355:11)

The errors returned from /GoodLog/web/save have the endpoint in the stack trace

VError: [error wrapper]: worst database ever
    at /Users/nigel/about.me/contextualizer/script.js:41:34
    at /Users/nigel/about.me/contextualizer/script.js:10:9
    at process._tickCallback (node.js:355:11)

And the errors from /GoodLog/api/save take it a step further and have a custom error message prepended to the passed error message

VError: error saving from API in good log router: worst database ever
    at /Users/nigel/about.me/contextualizer/script.js:49:34
    at /Users/nigel/about.me/contextualizer/script.js:10:9
    at process._tickCallback (node.js:355:11)

Not recommended but better than nothing

You can get lazy and pass the whole callback, not just the error, to contextualizer like this:

// fake library function
function databaseLookup(input, callback) {
    process.nextTick(function databaseInnards() {
        callback(new Error('this database is horrible'))
    })
}
 
// the function in which we want to add context to errors
function dataRequest(input, callback) {
    databaseLookup(input, function handleResponse(err, data) {
        // adds no context to errors, let's comment it out and not do it
        // databaseLookup(input, callback)
 
        // this is a little better
        databaseLookup(input, addContext(callback, 'error in dataRequest'))
    });
}

This is convenient but instead of handleResponse being in the stack trace, code within contextualizer will be the top frame. The line where you wrapped the callback is nowhere to be found. However, the error message will be prepended with your message. Like this:

VError: error in dataRequest: this database is horrible
    at contextualizerWrapper (/Users/nigel/about.me/contextualizer/index.js:21:28)
    at databaseInnards (/Users/nigel/about.me/contextualizer/script.js:73:9)
    at process._tickCallback (node.js:355:11)
    at Function.Module.runMain (module.js:503:11)
    at startup (node.js:129:16)
    at node.js:814:3

Package Sidebar

Install

npm i contextualizer

Weekly Downloads

14

Version

1.0.3

License

MIT

Last publish

Collaborators

  • nigelkibodeaux