sumologic
Version:
Sync Application logs to sumo logic directly via their http API
227 lines (184 loc) • 6.77 kB
JavaScript
var assert = require('assert');
var sinon = require('sinon');
var chai = require('chai')
, expect = chai.expect;
var SumoLogger = require('../sumo.js');
function newTestSumoLogger(fakeRequest) {
return new SumoLogger('FAKE-COLLECTOR-CODE', fakeRequest);
}
describe('Sumo Logic Collector', function() {
var clock;
beforeEach(function() { clock = sinon.useFakeTimers(); })
afterEach(function() { clock.restore(); })
function requestShouldntBeCalled() {
throw new Error("Unexpected call to request");
}
it("No sync for first second", function () {
var sumologic = newTestSumoLogger(requestShouldntBeCalled);
sumologic.log("log line");
clock.tick(1);
clock.tick(998);
});
it("No sync if there's no log data", function () {
var sumologic = newTestSumoLogger(requestShouldntBeCalled);
clock.tick(1);
clock.tick(1000);
clock.tick(1000);
});
it("Only one in progress request at any time", function () {
var count = 0
var sumologic = newTestSumoLogger(function() {
count += 1;
assert.equal(1, count);
});
sumologic.log("log line");
// First call to request
clock.tick(1000);
// Shouldn't call request, as we have an active call
clock.tick(1000);
});
it("Failed requests should be retried first", function () {
var expectedBody = ''
var sumologic = newTestSumoLogger(function(opts, cb) {
assert.equal(expectedBody , opts.body);
cb("some error");
});
sumologic.log("log line");
// First call to request, we should get a single line
expectedBody = '{"level":"INFO","data":"log line"}',
clock.tick(1000);
// Retry attempt - should have the same body as the previous request
expectedBody = '{"level":"INFO","data":"log line"}',
clock.tick(1000);
// Third retry attempt, should have 2 lines to sync (the original and the new line just added)
sumologic.log("new log line");
expectedBody = '{"level":"INFO","data":"log line"}\n{"level":"INFO","data":"new log line"}'
clock.tick(1000);
});
it("Non 200/300 status codes are treated as errors", function () {
var expectedBody = '';
var nextStatus = 0;
var sumologic = newTestSumoLogger(function(opts, cb) {
assert.equal(expectedBody, opts.body);
cb(undefined, {status: nextStatus});
});
sumologic.log("log line");
// First call to request, we should get a single line
nextStatus = 100;
expectedBody = '{"level":"INFO","data":"log line"}';
clock.tick(1000);
// Retry attempt - should have the same body as the previous request
nextStatus = 400;
expectedBody = '{"level":"INFO","data":"log line"}';
clock.tick(1000);
// Third retry attempt, should have 2 lines to sync (the original and the new line just added)
sumologic.log("new log line");
nextStatus = 500;
expectedBody = '{"level":"INFO","data":"log line"}\n{"level":"INFO","data":"new log line"}'
clock.tick(1000);
// attempts from now on sync all data, then request should no longer be called (as there are no log lines)
nextStatus = 200;
expectedBody = '{"level":"INFO","data":"log line"}\n{"level":"INFO","data":"new log line"}'
clock.tick(1000);
nextStatus = 200;
expectedBody = "ERROR, request shouldn't be called anymore";
clock.tick(1000);
});
it("Expected happy case of a few logs being synced every tick", function () {
var expectedLogLines = [];
var sumologic = newTestSumoLogger(function(opts, cb) {
expect(opts.body.split('\n')).to.deep.equal(expectedLogLines);
cb(undefined, {status: 200});
});
function logLine(line) {
expectedLogLines.push(JSON.stringify({level: 'INFO', data: line}));
sumologic.log(line);
}
function runSync() {
clock.tick(1000);
expectedLogLines = [];
}
logLine("msg 1");
logLine("msg 2");
logLine("msg 3");
runSync();
logLine("msg 3");
runSync();
logLine("msg 4");
logLine("msg 5");
logLine("msg 6");
logLine("msg 7");
runSync();
});
it("Ensure log lines are valid json", function () {
var expected = {};
var sumologic = newTestSumoLogger(function(opts, cb) {
expect(JSON.parse(opts.body)).to.deep.equal({level: "INFO", data: expected});
cb(undefined, {status: 200});
});
function check(obj) {
expected = obj;
sumologic.log(obj);
clock.tick(1000);
}
check("msg 1");
check({some: "values", and: "keys"});
check([1,2,3,4]);
function checkVarargs(obj1, obj2) {
expected = [obj1, obj2];
sumologic.log(obj1, obj2);
clock.tick(1000);
}
checkVarargs("msg 1", "msg 2");
checkVarargs("some string", {some: "values", and: "keys"});
checkVarargs("some string", [1,2,3,4], {});
});
it("Re-writes console.log correctly", function () {
var expected = {};
var sumologic = newTestSumoLogger(function(opts, cb) {
expect(JSON.parse(opts.body)).to.deep.equal({level: "INFO", data: expected});
cb(undefined, {status: 200});
});
function check(obj) {
expected = obj;
try {
sumologic.replaceConsole();
console.log(obj);
} finally {
sumologic.restoreConsole();
}
clock.tick(1000);
}
check("msg 1");
check({some: "values", and: "keys"});
check([1,2,3,4]);
});
it("Augments console.log correctly", function () {
var expected = {};
var sumologic = newTestSumoLogger(function(opts, cb) {
expect(JSON.parse(opts.body)).to.deep.equal({level: "INFO", data: expected});
cb(undefined, {status: 200});
});
sumologic.augmentConsole();
sinon.spy(sumologic, 'log');
sinon.spy(sumologic.stdConsole, 'log');
console.log("msg 1", "msg 2");
expect(sumologic.log.calledOnce, 'sumo/log').to.equal(true);
expect(sumologic.stdConsole.log.calledOnce, 'console/log').to.equal(true);
sinon.spy(sumologic, 'info');
sinon.spy(sumologic.stdConsole, 'info');
console.info("msg 1", "msg 2");
expect(sumologic.info.calledOnce, 'sumo/info').to.equal(true);
expect(sumologic.stdConsole.info.calledOnce, 'console/info').to.equal(true);
sinon.spy(sumologic, 'warn');
sinon.spy(sumologic.stdConsole, 'warn');
console.warn("msg 1", "msg 2");
expect(sumologic.warn.calledOnce, 'sumo/warn').to.equal(true);
expect(sumologic.stdConsole.warn.calledOnce, 'console/warn').to.equal(true);
sinon.spy(sumologic, 'error');
sinon.spy(sumologic.stdConsole, 'error');
console.error("msg 1", "msg 2");
expect(sumologic.error.calledOnce, 'sumo/error').to.equal(true);
expect(sumologic.stdConsole.error.calledOnce, 'console/error').to.equal(true);
});
});