twelve

Utilities to assist in building 12Factor apps

Usage no npm install needed!

<script type="module">
  import twelve from 'https://cdn.skypack.dev/twelve';
</script>

README

twelve

Utilities to assist in building 12Factor apps

npm install twelve

Usage

var twelve = require('twelve');

Utilities

III. Config

twelve.env(spec) can be used to parse environment variables into an application config object.

Example

Let's say we are writing an API service. In a shell set the variables as such:

export API_SERVER_PORT=3000
export API_SESSION_KEY=sid
export API_SESSION_SECRET=monkeys

Now in our service's codebase:

/* config.js */

var twelve = require('twelve');

module.exports = twelve.env({
    'api:server:port': {
        name: 'API_SERVER_PORT',
        parse: function(value) { return parseInt(value, 10); }
    },
    'api:session:key': 'API_SESSION_KEY',
    'api:session:secret':  'API_SESSION_SECRET',
    'log:dir': {
        name: 'API_LOG_DIR',
        required: false
    }
});


/* app.js */

var config = require('./config');

console.log(config.get('api:server:port'));  // should print 3000
console.log(config.get('api:session:key'));  // should print "sid"

Notice that there are two ways to map environment variables to runtime config variables. A key mapping to an object or a short-hand form of a key mapping to a string. The latter implies that the environment variable must be defined or the app will throw an error. The former allows us to define a parsing function and/or make environment variables optional.

XI. Logs

twelve.logger(serviceName, loggerName) is a simple log factory to create JSON loggers. All logging is done to stdout as per the documentation.

The factory caches loggers so repeatedly calling twelve.logger with same arguments will return the same logger instance.

log entries are automatically populated with an entry id, eid, using bson ObjectID. ObjectIDs are sortable and encode a timestamp, machine id and pid which is handy!

Levels

Loggers exposes five levels of logging that match the syslog spec

var logger = require('twelve').logger 'api';
logger.info('info message');
logger.warn('warning message');
logger.crit('critical message');  // logger.critical also available
logger.error('error message');
logger.debug('debug message');

Tagging requests

logger.withReq(<IncomingMessage>).info(...)

Loggers allows tagging a log entry with a request (http.IncomingMessage) which will amend the entry with useful information about the request.

var http = require('http');
var logger = require('twelve').logger 'api';

var server = http.createServer(function (req, res) {
    logger.withReq(req).debug('About to send hello world');
    response.writeHead(200, {"Content-Type": "text/plain"});
    response.end("Hello World\n");
});

server.listen(8000);

Example

/* app.js */

var twelve = require('twelve');
var config = require('./config');
var devlogger = twelve.logger('api', 'dev');

devlogger.info({message: 'Starting server on %s:%d', args: [config.get('api:host'), config.get('api:port')]});


/* user/routes.js */

var express = require('express');
var twelve = require('twelve');
var devlogger = twelve.logger('api', 'dev');
var logger = twelve.logger('api');  // returns the default logger for the api service

var router = module.exports = express.Router()

router.get('/', function(req, res) {
    devlogger.error({message: 'some sort of error', key: 'not_implemented'});
    logger.warn('attempt to access route that is not implemented');

    res.json(404, {message: 'go away'});
});

When running the service now, you will see JSON strings printed to stdout. tlog, a script provided with this project, can be used to prettify this output. This script is described below.

tlog

To complement the logging utility, a script is provided with twelve that can pretty-print log entries. One way to use it is to also install twelve globally using npm install -g twelve. Now you can run your service like so for example:

node app.js | tlog

bonus: tlog is smart about printing log entries where the message is a format string (using util.format). If you provided message and args keys in your log entries you will see this in action.

In the future, tlog will support filtering log entries by service and name.