Home Reference Source Repository

src/config/logger.js

// Import the neccesary modules.
import { logger as Logger } from 'express-winston';
import path from 'path';
import sprintf from 'sprintf';
import Winston from 'winston';

import { tempDir } from './constants';
import { name } from '../../package.json';

/**
 * The log levels Winston will be using.
 * @type {Object}
 */
const _levels = {
  error: 0,
  warn: 1,
  info: 2,
  debug: 3
};

/**
 * Check if the message is empty and replace it with the meta.
 * @param {Object} args - Arguments passed by Winston.
 * @returns {Object} - Formatter arguments passed by Winston.
 */
function _checkEmptyMessage(args) {
  if (args.message === '' && Object.keys(args.meta).length !== 0)
    args.message = JSON.stringify(args.meta);

  return args;
}

/**
 * Get the color of the output based on the log level.
 * @param {String} level - The log level.
 * @returns {String} - A color based on the log level.
 */
function _getLevelColor(level) {
  switch (level) {
  case 'error':
    return '\x1b[31m';
  case 'warn':
    return '\x1b[33m';
  case 'info':
    return '\x1b[36m';
  case 'debug':
    return '\x1b[34m';
  default:
    return '\x1b[36m';
  }
}

/**
 * Formatter function which formats the output to the console.
 * @param {Object} args - Arguments passed by Winston.
 * @returns {String} - The formatted message.
 */
function _consoleFormatter(args) {
  const newArgs = _checkEmptyMessage(args);
  const color = _getLevelColor(newArgs.level);

  return sprintf(`\x1b[0m[%s] ${color}%5s:\x1b[0m %2s/%d: \x1b[36m%s\x1b[0m`,
    new Date().toISOString(), newArgs.level.toUpperCase(), name,
    process.pid, newArgs.message);
}

/**
 * Formatter function which formate the output to the log file.
 * @param {Object} args - Arguments passed by Winston.
 * @returns {String} - The formatted message.
 */
function _fileFormatter(args) {
  const newArgs = _checkEmptyMessage(args);
  return JSON.stringify({
    name,
    pid: process.pid,
    level: newArgs.level,
    msg: newArgs.message,
    time: new Date().toISOString()
  });
}

/**
 * Create a Winston instance.
 * @external {Winston} https://github.com/winstonjs/winston
 * @returns {Wintson} - A configured Winston object.
 */
function _createWinston() {
  return new Winston.Logger({
    transports: [
      new Winston.transports.Console({
        name,
        levels: _levels,
        formatter: _consoleFormatter,
        handleExceptions: true,
        prettyPrint: true
      }),
      new Winston.transports.File({
        filename: path.join(tempDir, `${name}.log`),
        json: false,
        level: 'warn',
        formatter: _fileFormatter,
        maxsize: 5242880,
        handleExceptions: true
      })
    ],
    exitOnError: false
  });
}

/**
 * Create an Express Winston instance.
 * @external {ExpressWinston} http://bithavoc.io/express-winston/
 * @returns {ExpressWinston} - A configured Express Winston object.
 */
export function createExpressWinston() {
  return new Logger({
    winstonInstance: _createWinston(),
    expressFormat: true
  });
}

/**
 * Function to create a global logger object based on the properties of the logger function.
 * @param {?Boolean} [pretty] - Pretty mode for output with colors.
 * @param {?Boolean} [verbose] - Debug mode for no output.
 * @param {?winston} [winston=_createWinston()] - The Winston object to create a global logger.
 * @returns {void}
 */
export function createLogger(pretty, verbose, winston = _createWinston()) {
  if (!global.logger) global.logger = {};

  Object.keys(_levels).map(level => {
    if (pretty) {
      global.logger[level] = msg => {
        if (!verbose) winston[level](msg);
      };
    } else {
      global.logger[level] = msg => {
        if (!verbose) console[level](msg); // eslint-disable-line no-console
      };
    }
  });
}