All files / src/utils PrettyLogStream.js

21.62% Statements 8/37
0% Branches 0/19
0% Functions 0/6
21.62% Lines 8/37
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141    1x 1x 1x   1x                   1x                 1x                   1x                                                                                                                                                                                                               1x  
'use strict';
 
const bunyan = require('bunyan');
const chalk = require('chalk');
const Writable = require('stream').Writable;
 
const LOG_LEVELS = {
  UNKNOWN: 'Unknown',
  TRACE  : 'Trace',
  DEBUG  : 'Debug',
  INFO   : 'Info',
  WARN   : 'Warn',
  ERROR  : 'Error',
  FATAL  : 'Fatal',
};
 
const LOG_LEVELS_MAP = {
  [bunyan.TRACE]: LOG_LEVELS.TRACE,
  [bunyan.DEBUG]: LOG_LEVELS.DEBUG,
  [bunyan.INFO] : LOG_LEVELS.INFO,
  [bunyan.WARN] : LOG_LEVELS.WARN,
  [bunyan.ERROR]: LOG_LEVELS.ERROR,
  [bunyan.FATAL]: LOG_LEVELS.FATAL,
};
 
const LOG_LEVELS_COLORS = {
  [LOG_LEVELS.UNKNOWN]: 'white',
  [LOG_LEVELS.TRACE]  : 'grey',
  [LOG_LEVELS.DEBUG]  : 'cyan',
  [LOG_LEVELS.INFO]   : 'green',
  [LOG_LEVELS.WARN]   : 'yellow',
  [LOG_LEVELS.ERROR]  : 'red',
  [LOG_LEVELS.FATAL]  : 'magenta',
};
 
const HTTP_COLORS = {
  DEFAULT: 'white',
  '200'  : 'green',
  '300'  : 'yellow',
  '400'  : 'red',
  '500'  : 'magenta',
};
 
class PrettyLogStream extends Writable {
  /** @inheritdoc */
  _write(entry, encoding, done) {
    process.stdout.write(this._formatEntry(entry));
    done();
  }
 
  /**
   * Format a log entry.
   * @param {Object} entry - Bunyan log entry
   * @return {string} Formatted log entry
   * @private
   */
  _formatEntry(entry) {
    try {
      // Bunyan log entries are JSON encoded
      entry = JSON.parse(entry);
    } catch (e) {
      // In case we could not decode the entry, create a basic object containing
      // the entry itself as a message.
      entry = {msg: entry};
    }
 
    // Extract informations from log entry
    const entryTime = entry.time || (new Date()).toISOString();
    const entryLevel = this._getLogLevelName(entry.level);
    const entryType = entry.type;
    const entryMessage = entry.msg;
 
    // Retrieve the log message based on the type of the entry
    let content = '';
    switch (entryType) {
    case 'request':
      content = this._formatRequest(entry);
      break;
    case 'response':
      content = this._formatResponse(entry);
      break;
    default:
      content = `${chalk[LOG_LEVELS_COLORS[entryLevel]](entryMessage)}`;
      break;
    }
 
    return `${entryTime} (${chalk.bold[LOG_LEVELS_COLORS[entryLevel]](entryLevel)}) ${content}\n`
  }
 
  /**
   * Format a "request" log entry
   * @param {Object} entry - Log entry previously parsed by _formatEntry
   * @return {string} Formatted message
   * @private
   */
  _formatRequest(entry) {
    const method = entry.method || '?';
    const url = entry.url || '?';
 
    return `${chalk.white.bold('▶')} ${chalk.bold(method)} ${url}`
  }
 
  /**
   * Format a "response" log entry
   * @param {Object} entry - Log entry previously parsed by _formatEntry
   * @return {string} Formatted message
   * @private
   */
  _formatResponse(entry) {
    const method = entry.method || '?';
    const url = entry.url || '?';
    const statusCode = entry.httpCode || 0;
    const statusColor = this._getHttpStatusColor(statusCode);
 
    return `${chalk.bold[statusColor]('◀')} ${chalk.bold(method)} ${url} [${chalk.bold[statusColor](statusCode)}]`;
  }
 
  /**
   * Return the name of a bunyan log level.
   * @param {number} logLevel - A bunyan log level
   * @return {string} Level name
   * @private
   */
  _getLogLevelName(logLevel) {
    return LOG_LEVELS_MAP.hasOwnProperty(logLevel) ? LOG_LEVELS_MAP[logLevel] : LOG_LEVELS.UNKNOWN;
  }
 
  /**
   * Return a color given a HTTP status code.
   * @param {number} statusCode - HTTP status code
   * @return {string} Chalk color name
   * @private
   */
  _getHttpStatusColor(statusCode) {
    const codeCategory = Math.floor(statusCode / 100) * 100;
    return HTTP_COLORS.hasOwnProperty(codeCategory) ? HTTP_COLORS[codeCategory] : HTTP_COLORS.DEFAULT;
  }
}
 
module.exports = PrettyLogStream;