log4js-cloudwatch-appender

1.1.0 • Public • Published

Build Status Coverage Status bitHound Overall Score Known Vulnerabilities dependencies Status devDependencies Status npm HitCount

https://nodei.co/npm/log4js-cloudwatch-appender.png?downloads=true&downloadRank=true&stars=true

log4js-cloudwatch-appender

Simple appender for log4js to submit logs to AWS cloudwatch based on the lawgs module.

Installation

This module is installed via npm:

npm install --save log4js-cloudwatch-appender

Usage

Add aws appender to the log4js config file:

const config = {
    appenders: {
        aws: {
              type: "log4js-cloudwatch-appender",
              accessKeyId: '<accessKeyId>',
              secretAccessKey: '<secretAccessKey>',
              region: 'eu-central-1',
              logGroup: 'prod',
              logStream: 'apps',
              layout: '<custom layout object>',
              lawgsConfig: '<optional alwgs config object>'
            }
    },
    categories: {
        default: {appenders: ['aws'], level: 'info'}
    }
}

This will cause logs to be sent to AWS CloudWatch with the specified group and stream.

Configuration

If you are using roles, you will need the following roles:

  • logs:DescribeLogGroups
  • logs:DescribeLogStreams
  • logs:CreateLogGroup
  • logs:CreateLogStream
  • logs:PutLogEvents

mandatory

  • region - The CloudWatch region
  • logGroup - The log group to send the metrics to
  • logStream - The log stream of the group to send the metrics to

optional

  • accessKeyId - Optional if credentials are set in ~/.aws/credentials
  • secretAccessKey - Optional if credentials are set in ~/.aws/credentials
  • layout - Custom layout. See suggested layout
  • lawgsConfig - Optional vonfig object for lawgs:
    • showDebugLogs - Show debug logs. Default: false.
    • uploadMaxTimer - After this ms timeout, flush to server. Default: 5000.
    • uploadBatchSize - After this amount of logs, flush to server. Default: 500.

Suggested json layout

Logs are easier to query whn they are formatted as json. Following is a suggested json layout to set for this appender. The logging style should be:

const uuid = require('node-uuid');
const corr = uuid.v4();
const logger = logFactory.getLogger('category');
 
logger.info(corr, 'methodName()','part1','part2');

Which will output:

{
  "timestamp": "2017-06-10T11:55:38.251Z",
  "corr": "2e2c99aa-7eee-4fd2-ae36-cd9dc9533816",
  "app": "<appName>",
  "host": "<ip>",
  "pid": 24532,
  "level": "INFO",
  "category": "category",
  "method": "methodName()",
  "message": "part1 part2"
}

The layout:

const util = require('util');
const _ = require('underscore');
 
let processName = path.basename(process.argv[1]);
processName = processName.substring(0, processName.length - 3);
 
const publicIp = require('public-ip').v4;
let ip = '';
publicIp()
  .then(function (_ip) {
    ip = _ip;
  })
  .catch(function (e) {
    console.log(e);
    ip = 'unknown';
  });
 
const jsonLayout = {
  "type": "pattern",
  "pattern": '{"timestamp": "%d{yyyy-MM-ddThh:mm:ss.SSSZ}", "app": "' + processName + '", "ip": "%x{my_ip}", "host": "%h", "pid": %z, "level": "%p", "category": "%c"%x{corr}%x{method}, "message": "%x{message}"}',
  "tokens": {
    "my_ip": function () {
      return ip;
    },
   "corr": function (logEvent) {
      logEvent.__data__ = _.map(logEvent.data, _.clone);
      if (logEvent.__data__) {
        let corr = logEvent.__data__[0];
        if (Array.isArray(corr) && corr.length === 2) {
          corr = corr[0];
          if (typeof corr === 'string' && corr.length === 36 && corr.split("-").length === 5) {
            logEvent.__data__[0] = logEvent.__data__[0][1];
            return ', "corr": "' + corr + '"';
          }
        }
        if (logEvent.__data__.length > 1 && corr && typeof corr === 'string' && corr.length === 36 && corr.split("-").length === 5) {
          logEvent.__data__.shift();
          return ', "corr": "' + corr + '"';
        }
      }
      return '';
    },
    "method": function (logEvent) {
      if (logEvent.__data__) {
        const method = logEvent.__data__[0];
        if (logEvent.__data__.length > 1 && method && typeof method === 'string' && method.indexOf("()", method.length - 2) !== -1) {
          logEvent.__data__.shift();
          return ', "method": "' + method + '"';
        }
      }
      return '';
    },
    "message": function (logEvent) {
      if (logEvent.__data__) {
        let data = logEvent.__data__;
        data = util.format.apply(util, wrapErrorsWithInspect(data));
        data = escapedStringify(data);
        logEvent.__data__ = undefined;
        return data;
      }
      return '';
    }
  }
};
 
function wrapErrorsWithInspect(items) {
  return items.map(function (item) {
    if ((item instanceof Error) && item.stack) {
      return {
        inspect: function () {
          return util.format(item) + '\n' + item.stack;
        }
      };
    } else {
      return item;
    }
  });
}
 
function escapedStringify(json) {
  return json
    .replace(/[\\]/g, '\\\\')
    .replace(/[\"]/g, '\\\"')
    .replace(/[\/]/g, '\\/')
    .replace(/[\b]/g, '\\b')
    .replace(/[\f]/g, '\\f')
    .replace(/[\n]/g, '\\n')
    .replace(/[\r]/g, '\\r')
    .replace(/[\t]/g, '\\t');
}

Contributing

Please make all pull requests to the master branch and ensure tests pass locally.

Package Sidebar

Install

npm i log4js-cloudwatch-appender

Weekly Downloads

470

Version

1.1.0

License

GPL-3.0

Last publish

Collaborators

  • regevbr