blob: 84c41d133de2610290eefb0ec04676e5330c6522 [file] [log] [blame]
/* eslint no-prototype-builtins:1,no-restricted-syntax:[1, "ForInStatement"] */
'use strict';
const debug = require('debug')('log4js:loggly');
const loggly = require('loggly');
const os = require('os');
function isAnyObject(value) {
return value !== null && (typeof value === 'object' || typeof value === 'function');
}
function numKeys(obj) {
return Object.keys(obj).length;
}
/**
* @param msgListArgs
* @returns Object{ deTaggedMsg: [...], additionalTags: [...] }
*/
function processTags(msgListArgs) {
const msgList = (msgListArgs.length === 1 ? [msgListArgs[0]] : msgListArgs);
return msgList.reduce((accumulate, element) => {
if (isAnyObject(element) && Array.isArray(element.tags) && numKeys(element) === 1) {
accumulate.additionalTags = accumulate.additionalTags.concat(element.tags);
} else {
accumulate.deTaggedData.push(element);
}
return accumulate;
}, { deTaggedData: [], additionalTags: [] });
}
/**
* Loggly Appender. Sends logging events to Loggly using node-loggly, optionally adding tags.
*
* This appender will scan the msg from the logging event, and pull out any argument of the
* shape `{ tags: [] }` so that it's possible to add tags in a normal logging call.
*
* For example:
*
* logger.info({ tags: ['my-tag-1', 'my-tag-2'] }, 'Some message', someObj, ...)
*
* And then this appender will remove the tags param and append it to the config.tags.
*
* @param config object with loggly configuration data
* {
* token: 'your-really-long-input-token',
* subdomain: 'your-subdomain',
* tags: ['loggly-tag1', 'loggly-tag2', .., 'loggly-tagn']
* }
* @param layout a function that takes a logevent and returns a string (defaults to objectLayout).
*/
function logglyAppender(config, layout) {
const client = loggly.createClient(config);
let openRequests = 0;
let shutdownCB;
debug('creating appender.');
function app(loggingEvent) {
const result = processTags(loggingEvent.data);
const deTaggedData = result.deTaggedData;
const additionalTags = result.additionalTags;
// Replace the data property with the deTaggedData
loggingEvent.data = deTaggedData;
const msg = layout(loggingEvent);
openRequests += 1;
debug('sending log event to loggly');
client.log(
{
msg: msg,
level: loggingEvent.level.levelStr,
category: loggingEvent.categoryName,
hostname: os.hostname().toString(),
},
additionalTags,
(error) => {
if (error) {
console.error('log4js.logglyAppender - error occurred: ', error);
}
debug('log event received by loggly.');
openRequests -= 1;
if (shutdownCB && openRequests === 0) {
shutdownCB();
shutdownCB = undefined;
}
}
);
}
app.shutdown = function (cb) {
debug('shutdown called');
if (openRequests === 0) {
cb();
} else {
shutdownCB = cb;
}
};
return app;
}
function configure(config, layouts) {
let layout = layouts.messagePassThroughLayout;
if (config.layout) {
layout = layouts.layout(config.layout.type, config.layout);
}
debug('configuring new appender');
return logglyAppender(config, layout);
}
module.exports.configure = configure;