Nantes Paradox Market

    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
    

    Install

    npm i contextualizer

    DownloadsWeekly Downloads

    5

    Version

    1.0.3

    License

    MIT

    Last publish

    Collaborators

    • nigelkibodeaux