Package Exports
- log4js-cloudwatch-appender
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (log4js-cloudwatch-appender) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
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 regionlogGroup
- The log group to send the metrics tologStream
- 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 layoutlawgsConfig
- 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.