log4js-elasticsearch
Version:
log4js appender for node that targets elasticsearch. Compatible with logstash's elasticsearch_http output; Viewable with Kibana.
287 lines (270 loc) • 8.74 kB
JavaScript
var expect = require('chai').expect;
var sandbox = require('sandboxed-module');
var libpath = process.env.COVERAGE ? '../lib-cov' : '../lib';
var log4jsElasticSearch = require(libpath + '/log4js-elasticsearch');
describe('When configuring a logger posting events to elasticsearch', function() {
var log4js = require('log4js');
var mockElasticsearchClient = {
index: function(indexName, typeName, logObj, newLogId, cb) {
expect(indexName).to.match(/^logstash-/);
expect(typeName).to.equal('nodejs');
expect(newLogId).to.not.exist;
expect(logObj['@fields'].category).to.equal('unittest');
expect(logObj['@source']).to.equal('log4js');
expect(logObj['@source_host']).to.equal(require('os').hostname());
expect(logObj['@source_path']).to.equal(process.cwd());
expect(logObj['@tags'].length).to.equal(0);
if (currentMsg) {
expect(logObj['@message']).to.equal(currentMsg);
currentMsg = null;
} else {
expect(logObj['@message']).to.exist;
}
if (currentErrorMsg) {
expect(currentErrorMsg).to.equal(logObj['@fields'].error);
expect(logObj['@fields'].stack).to.be['instanceof'](Array);
currentErrorMsg = null;
}
if (currentLevelStr) {
expect(logObj['@fields'].levelStr).to.equal(currentLevelStr);
currentLevelStr = null;
}
if (currentCallback) {
var callit = currentCallback;
currentCallback = null;
callit();
}
cb();
}, defineTemplate: function(templateName, template, done) {
expect(templateName).to.equal('logstash-*');
defineTemplateWasCalled = true;
done();
}, getTemplate: function(templateName, cb) {
cb(null, '{}');
}
};
var currentMsg;
var currentCallback;
var currentErrorMsg;
var currentLevelStr;
var defineTemplateWasCalled = false;
before(function(done) {
var config = { esclient: mockElasticsearchClient, buffersize: 1 };
log4js.clearAppenders();
log4js.addAppender(log4jsElasticSearch.configure(config, null, done), 'unittest');
});
it("Must have created the template", function() {
expect(defineTemplateWasCalled).to.equal(true);
});
describe("When logging", function() {
it('Must send events to elasticsearch', function(done) {
var log = log4js.getLogger('unittest');
var nolog = log4js.getLogger('notunittest');
currentErrorMsg = 'I should not be called at all';
nolog.error('nono');
currentErrorMsg = null;
currentLevelStr = 'ERROR';
currentMsg = 'aha';
log.error('aha');
expect(currentMsg).to.be['null'];
currentLevelStr = 'INFO';
currentMsg = 'huhu \'hehe\'';
log.info('huhu', 'hehe');
expect(currentMsg).to.be['null'];
currentLevelStr = 'WARN';
currentMsg = 'ohoho';
currentErrorMsg = 'pants on fire';
log.warn('ohoho', new Error('pants on fire'));
currentCallback = done;
currentMsg = 'ohoho a param';
currentErrorMsg = 'pants on fire';
log.error('ohoho %s', 'a param', new Error('pants on fire'));
});
});
});
describe('When configuring an elasticsearch appender', function() {
var log4js = sandbox.require('log4js', {
requires: {
'log4js-elasticsearch': log4jsElasticSearch
}
});
var currentMsg;
var defineTemplateWasCalled = false;
var mockElasticsearchClient = {
index: function(indexName, typeName, logObj, id, cb) {
expect(logObj['@message']).to.equal(currentMsg);
currentMsg = null;
cb();
}, defineTemplate: function(templateName, template, cb) {
defineTemplateWasCalled = true;
cb(null, 'ok');
}, getTemplate: function(templateName, cb) {
cb(null, '{}');
}
};
before(function() {
log4js.configure({
"appenders": [
{
"type": "log4js-elasticsearch",
"esclient": mockElasticsearchClient,
"buffersize": 1,
"layout": { type: 'logstash' }
}
]
});
expect(defineTemplateWasCalled).to.be['true'];
});
it('Must have configured the appender', function() {
currentMsg = 'hello';
log4js.getLogger('unittest').info('hello');
expect(currentMsg).to.be['null'];
});
});
describe('When configuring an elasticsearch logstash appender layout', function() {
var log4js = sandbox.require('log4js', {
requires: {
'log4js-elasticsearch': log4jsElasticSearch
}
});
var currentMsg;
var defineTemplateWasCalled = false;
var expectedNumberOfShardsInTemplate = -1;
var mockElasticsearchClient = {
index: function(indexName, typeName, logObj, id, cb) {
expect(logObj['@message']).to.equal(currentMsg);
expect(logObj['@tags'][0]).to.equal('goodie');
expect(logObj['@source_host']).to.equal('aspecialhost');
expect(typeName).to.equal('customType');
currentMsg = null;
cb();
}, defineTemplate: function(templateName, template, cb) {
defineTemplateWasCalled = true;
if (expectedNumberOfShardsInTemplate !== -1) {
expect(template.settings.number_of_shards).to.equal(expectedNumberOfShardsInTemplate);
expectedNumberOfShardsInTemplate = -1;
}
cb(null, 'something');
}, getTemplate: function(templateName, cb) {
cb(null, '{}');
}
};
it('Must have configured the appender with static params', function() {
expectedNumberOfShardsInTemplate = 1;
log4js.configure({
"appenders": [
{
"type": "log4js-elasticsearch",
"esclient": mockElasticsearchClient,
"typeName": "customType",
"buffersize": 1,
"layout": {
"type": "logstash",
"tags": [ "goodie" ],
"sourceHost": "aspecialhost",
"template": {"settings": { "number_of_shards": 1 }}
}
}
]
});
expect(defineTemplateWasCalled).to.be['true'];
defineTemplateWasCalled = undefined;
currentMsg = 'hello';
log4js.getLogger('unittest').info('hello');
expect(currentMsg).to.be['null'];
});
it('Must have configured the appender with dynamic params', function() {
expectedNumberOfShardsInTemplate = 4;
log4js.configure({
"appenders": [
{
"type": "log4js-elasticsearch",
"esclient": mockElasticsearchClient,
"buffersize": 1,
"typeName": function(loggingEvent) {
return 'customType';
},
"layout": {
"type": "logstash",
"tags": function(loggingEvent) {
return [ 'goodie' ];
},
"sourceHost": function(loggingEvent) {
return "aspecialhost";
}
}
}
]
});
expect(defineTemplateWasCalled).to.be['true'];
defineTemplateWasCalled = undefined;
currentMsg = 'hello';
log4js.getLogger('unittest').info('hello');
expect(currentMsg).to.be['null'];
});
});
describe('When sending the logs in bulk', function() {
var log4js = sandbox.require('log4js', {
requires: {
'log4js-elasticsearch': log4jsElasticSearch
}
});
var currentMsg;
var bulkWasCalled = 0;
var expectedBulkcmdsSize;
var mockElasticsearchClient = {
bulk: function(bulkCmds, cb) {
if (expectedBulkcmdsSize) {
expect(bulkCmds.length).to.equal(expectedBulkcmdsSize);
expectedBulkcmdsSize = null;
}
bulkWasCalled++;
cb();
}, getTemplate: function(templateName, cb) {
cb(null, 'notempty');
}
};
function reset(done) {
log4jsElasticSearch.flushAll(true);
bulkWasCalled = 0;
done();
}
afterEach(reset);
beforeEach(reset);
it('Must send logs in bulks when the bufferSize is reached', function(done) {
log4js.configure({
"appenders": [
{
"type": "log4js-elasticsearch",
"esclient": mockElasticsearchClient,
"buffersize": 2
}
]
});
expectedBulkcmdsSize = 4;
log4js.getLogger('unittest').info('hello');
expect(bulkWasCalled).to.equal(0);
log4js.getLogger('unittest').info('goodbye');
expect(bulkWasCalled).to.equal(1);
done();
});
it('Must send logs in bulks after a timeout', function(done) {
log4js.configure({
"appenders": [
{
"type": "log4js-elasticsearch",
"esclient": mockElasticsearchClient,
"buffersize": 2,
"timeout": 20
}
]
});
expectedBulkcmdsSize = 2;
log4js.getLogger('unittest').info('hello');
expect(bulkWasCalled).to.equal(0);
setTimeout(function() {
expect(bulkWasCalled).to.equal(1);
done();
}, 80);
});
});