sumologic
Version:
Sync Application logs to sumo logic directly via their http API
116 lines (97 loc) • 3.28 kB
JavaScript
// Log to sumo logic directly
var _ = require('underscore');
var safeStringify = require('./safe-stringify');
module.exports = function SumoLogger(collectorCode, opts) {
var me = this;
opts = opts || {};
var endpoint = opts.endpoint || "https://collectors.au.sumologic.com/receiver/v1/http/";
var request = opts.request || require('request');
var collectorEndpoint = endpoint + collectorCode;
var syncInterval = opts.syncInterval || 1000;
me.stdConsole = {
log: console.log,
info: console.info,
error: console.error,
warn: console.warn
}
me.replaceConsole = function() {
console.log = me.log;
console.info = me.info;
console.error = me.error;
console.warn = me.warn;
}
me.restoreConsole = function() {
console.log = me.stdConsole.log;
console.info = me.stdConsole.info;
console.error = me.stdConsole.error;
console.warn = me.stdConsole.warn;
}
me.augmentConsole = function() {
console.log = function() {
me.stdConsole.log.apply(this, arguments);
me.log.apply(this, arguments);
}
console.info = function() {
me.stdConsole.info.apply(this, arguments);
me.info.apply(this, arguments);
}
console.warn = function() {
me.stdConsole.warn.apply(this, arguments);
me.warn.apply(this, arguments);
}
console.error = function() {
me.stdConsole.error.apply(this, arguments);
me.error.apply(this, arguments);
}
}
// Cache of entries we are yet to sync
var unsynced = [];
function append(lvl, args) {
var logentry = {level: lvl}
// In the common case of a single log value, pull it out. It's easier in sumo
// logic to traverse known object graphs without arrays, especially at the
// top level
if (args.length == 1) {
logentry.data = args[0];
} else {
logentry.data = args;
}
unsynced.push(safeStringify(logentry));
}
// I want arguments to be treated as an object (helps indexing into the correct fields on sumo logic)
me.log = function() { append('INFO', arguments); };
me.info = function() { append('INFO', arguments); };
me.error = function() { append('ERROR', arguments); };
me.warn = function() { append('WARN', arguments); };
var numBeingSent = 0;
var maxLines = 100;
// explicit decision to *not* use debounce/throttle so the syncing code is
// explicit, and it's possible for a human to prove it's correctness
var syncer = setInterval(function() {
// Only one active sync at any given interval
if (numBeingSent > 0) {
return;
}
// Short-circuit if there is nothing to send
if (unsynced.length == 0) {
return;
}
var logLines = unsynced.slice(0, maxLines);
var body = logLines.join('\n')
numBeingSent = logLines.length;
// Sync logs to sumo-logic, and clear all synced data. On failure we'll retry the sync
request({
method: 'POST',
url: collectorEndpoint,
body: body,
}, function(error, response) {
var failed = !!error ||
response.status < 200 ||
response.status >= 400;
if (!failed) {
unsynced.splice(0, numBeingSent);
}
numBeingSent = 0;
})
}, syncInterval);
}