MorganJS is easy to install and works nicely out of the box.

var Express = require('express');
var app = Express();
// ...
var Morgan = require('morgan');
app.use(Morgan('dev'));
// ...
app.listen(port);
console.log('Magic happens on port ' + port + ' since ' + (new Date));

Here is what it looks like. Highlighted HTTP status codes are quite useful.

Screen Shot 2015-11-08 at 14.44.07

Thankfully, it’s possible to customize MorganJS by adding tokens, which are template symbols, like this:

Morgan.token('current-user', function(req, res) {
    var result = req.currentUser ? req.currentUser.name : 'Anonymous';
    return result;
});

which can be later used like this:

// ...
var Morgan = require('morgan');
app.use(Morgan(':date[iso] :current-user :method :url :status :response-time ms - :res[content-length] bytes'));
// ...

to produce something like this:

Screen Shot 2015-11-08 at 14.52.16

Uh-oh!! Where are my colors?

I delve into MorganJS code…

/**
 * dev (colored)
 */

morgan.format('dev', function developmentFormatLine(tokens, req, res) {
  // get the status code if response written
  var status = res._header
    ? res.statusCode
    : undefined;

  // get status color
  var color = status >= 500 ? 31 // red
    : status >= 400 ? 33 // yellow
    : status >= 300 ? 36 // cyan
    : status >= 200 ? 32 // green
    : 0; // no color

  // get colored function
  var fn = developmentFormatLine[color];

  if (!fn) {
    // compile
    fn = developmentFormatLine[color] = compile('\x1b[0m:method :url \x1b['
      + color + 'm:status \x1b[0m:response-time ms - :res[content-length]\x1b[0m');
  }

  return fn(tokens, req, res);
})

As you may have noticed, the above code is hard to understand and quite hard-coded too.

  • Hard-coded because, even if the ´dev´ template is documented as ´:method :url :status :response-time ms – :res[content-length]´, it’s really embedded into the code and mixed up with extraneous bits rather than being declared into some option and used like any other MorganJS template is.
  • Hard to understand because the function object is being used as a cache for its own executions which entail a compilation step whose raison d’être I still have to grasp. I could be wrong, but this one could be a clear example of over-engineering.

However my biggest disappointment was that there is no way of reusing the colored ´:status´ token nor the coloring functionality, neither directly, by calling a method, nor indirectly, by copy-pasting some code. A total fail. :-(

Googling “terminal colors” I eventually got to this Unix StackExchange answer, which I used to write this:

function ColorFactory() {
    var result = {};

    /* beautify preserve:start */
    result.DEFAULT      = '\x1b[0m';
    result.WHITE        = '\x1b[1;37m';
    result.BLACK        = '\x1b[0;30m';
    result.BLUE         = '\x1b[0;34m';
    result.LIGHT_BLUE   = '\x1b[1;34m';
    result.GREEN        = '\x1b[0;32m';
    result.LIGHT_GREEN  = '\x1b[1;32m';
    result.CYAN         = '\x1b[0;36m';
    result.LIGHT_CYAN   = '\x1b[1;36m';
    result.RED          = '\x1b[0;31m';
    result.LIGHT_RED    = '\x1b[1;31m';
    result.PURPLE       = '\x1b[0;35m';
    result.LIGHT_PURPLE = '\x1b[1;35m';
    result.BROWN        = '\x1b[0;33m';
    result.YELLOW       = '\x1b[1;33m';
    result.GRAY         = '\x1b[0;30m';
    result.LIGHT_GRAY   = '\x1b[0;37m';
    /* beautify preserve:end */

    // ['DEFAULT', 'WHITE', 'BLACK', ...]
    result.allNames = Object.keys(result);

    // ['\x1b[0m', '\x1b[1;37m', '\x1b[0;30m', ...]
    result.allValues = result.allNames.map(function(name) {
        return result[name];
    });

    var anyValue = result.allValues.map(function(value) {
        return value.replace(/\W/g, '\\$&');
    }).join('|');
    var anySequenceOfValues = new RegExp('(?:' + result.anyValue + ')+(' + result.anyValue + ')', 'g');
    var lastValueOfSequence = '$1';

    function paint(COLOR) {
        return function(string) {
            var colored = COLOR + string + result.DEFAULT;
            var simplified = colored.replace(anySequenceOfValues, lastValueOfSequence);
            return simplified;
        }
    }

    /* beautify preserve:start */
    result.Default     = paint(result.DEFAULT);
    result.White       = paint(result.WHITE);
    result.Black       = paint(result.BLACK);
    result.Blue        = paint(result.BLUE);
    result.LightBlue   = paint(result.LIGHT_BLUE);
    result.Green       = paint(result.GREEN);
    result.LightGreen  = paint(result.LIGHT_GREEN);
    result.Cyan        = paint(result.CYAN);
    result.LightCyan   = paint(result.LIGHT_CYAN);
    result.Red         = paint(result.RED);
    result.LightRed    = paint(result.LIGHT_RED);
    result.Purple      = paint(result.PURPLE);
    result.LightPurple = paint(result.LIGHT_PURPLE);
    result.Brown       = paint(result.BROWN);
    result.Yellow      = paint(result.YELLOW);
    result.Gray        = paint(result.GRAY);
    result.LightGray   = paint(result.LIGHT_GRAY);
    /* beautify preserve:end */

    return result;
}

A nice collateral about my ´ColorFactory´ function is that I can use it also in the console like this:

var color = ColorFactory();
console.log('Magic happens on port ' + color.Purple(port) + ' since ' + (new Date));

to get something like this:

Screen Shot 2015-11-08 at 16.12.17

Finally, I was able to customize Morgan with this:

function MorganFactory() {
    var color = ColorFactory();

    var Morgan = require('morgan');

    Morgan.token('current-user', function(req, res, type) {
        var result = req.currentUser ? req.currentUser.name : 'Anonymous';
        if ('colored' == type) {
            result = req.currentUser ? color.LightBlue(req.currentUser.name) : color.LightRed('Anonymous');
        }
        return result;
    });

    var defaultStatusToken = Morgan['status'];
    Morgan.token('status', function(req, res, type) {
        var status = defaultStatusToken(req, res);
        if ('colored' == type) {
            /* beautify preserve:start */
            var result =
                  status >= 500 ? color.Red(status)
                : status >= 400 ? color.Yellow(status)
                : status >= 300 ? color.Cyan(status)
                : status >= 200 ? color.Green(status)
                : status;
            /* beautify preserve:end */            
        } else {
            result = status;
        }
        return result;
    });

    return Morgan.apply(null, arguments);
}

and use it like this:

// ...
var Morgan = MorganFactory;
app.use(Morgan(':date[iso] :current-user[colored] :method :url :status[colored] :response-time ms - :res[content-length] bytes'));
// ...

to get something like this:

Screen Shot 2015-11-08 at 16.51.54