/** * Module dependencies. */ 'use strict'; const Counter = require('passthrough-counter'); const humanize = require('humanize-number'); const bytes = require('bytes'); const chalk = require('chalk'); /** * TTY check for dev format. */ const isatty = process.stdout.isTTY; /** * Expose logger. */ module.exports = dev; /** * Color map. */ const colorCodes = { 5: 'red', 4: 'yellow', 3: 'cyan', 2: 'green', 1: 'green', 0: 'yellow' }; /** * Development logger. */ function dev(opts) { return function logger(ctx, next) { // request const start = new Date; console.log(' ' + chalk.gray('<--') + ' ' + chalk.bold('%s') + ' ' + chalk.gray('%s'), ctx.method, ctx.originalUrl); return next().then(function() { // calculate the length of a streaming response // by intercepting the stream with a counter. // only necessary if a content-length header is currently not set. const length = ctx.response.length; const body = ctx.body; let counter; if (null == length && body && body.readable) { ctx.body = body .pipe(counter = Counter()) .on('error', ctx.onerror); } // log when the response is finished or closed, // whichever happens first. const res = ctx.res; const onfinish = done.bind(null, 'finish'); const onclose = done.bind(null, 'close'); res.once('finish', onfinish); res.once('close', onclose); function done(event){ res.removeListener('finish', onfinish); res.removeListener('close', onclose); log(ctx, start, counter ? counter.length : length, null, event); } }, function(err) { // log uncaught downstream errors log(ctx, start, null, err); throw err; }); } } /** * Log helper. */ function log(ctx, start, len, err, event) { // get the status code of the response const status = err ? (err.status || 500) : (ctx.status || 404); // set the color of the status code; const s = status / 100 | 0; const color = colorCodes[s]; // get the human readable response length let length; if (~[204, 205, 304].indexOf(status)) { length = ''; } else if (null == len) { length = '-'; } else { length = bytes(len); } const upstream = err ? chalk.red('xxx') : event === 'close' ? chalk.yellow('-x-') : chalk.gray('-->') console.log(' ' + upstream + ' ' + chalk.bold('%s') + ' ' + chalk.gray('%s') + ' ' + chalk[color]('%s') + ' ' + chalk.gray('%s') + ' ' + chalk.gray('%s'), ctx.method, ctx.originalUrl, status, time(start), length); } /** * Show the response time in a human readable format. * In milliseconds if less than 10 seconds, * in seconds otherwise. */ function time(start) { const delta = new Date - start; return humanize(delta < 10000 ? delta + 'ms' : Math.round(delta / 1000) + 's'); }