diff --git a/lib/appenders/gelf.js b/lib/appenders/gelf.js index e0c7fc2..ebeff38 100644 --- a/lib/appenders/gelf.js +++ b/lib/appenders/gelf.js @@ -4,6 +4,25 @@ var levels = require('../levels'); var dgram = require('dgram'); var util = require('util'); +var LOG_EMERG=0; // system is unusable +var LOG_ALERT=1; // action must be taken immediately +var LOG_CRIT=2; // critical conditions +var LOG_ERR=3; // error conditions +var LOG_ERROR=3; // because people WILL typo +var LOG_WARNING=4; // warning conditions +var LOG_NOTICE=5; // normal, but significant, condition +var LOG_INFO=6; // informational message +var LOG_DEBUG=7; // debug-level message + +var levelMapping = {}; +levelMapping[levels.ALL] = LOG_DEBUG; +levelMapping[levels.TRACE] = LOG_DEBUG; +levelMapping[levels.DEBUG] = LOG_DEBUG; +levelMapping[levels.INFO] = LOG_INFO; +levelMapping[levels.WARN] = LOG_WARNING; +levelMapping[levels.ERROR] = LOG_ERR; +levelMapping[levels.FATAL] = LOG_CRIT; + /** * GELF appender that supports sending UDP packets to a GELF compatible server such as Graylog * @@ -15,32 +34,11 @@ var util = require('util'); */ function gelfAppender (layout, host, port, hostname, facility) { - var logEventBuffer = []; - - LOG_EMERG=0; // system is unusable - LOG_ALERT=1; // action must be taken immediately - LOG_CRIT=2; // critical conditions - LOG_ERR=3; // error conditions - LOG_ERROR=3; // because people WILL typo - LOG_WARNING=4; // warning conditions - LOG_NOTICE=5; // normal, but significant, condition - LOG_INFO=6; // informational message - LOG_DEBUG=7; // debug-level message - - var levelMapping = {}; - levelMapping[levels.ALL] = LOG_DEBUG; - levelMapping[levels.TRACE] = LOG_DEBUG; - levelMapping[levels.DEBUG] = LOG_DEBUG; - levelMapping[levels.INFO] = LOG_INFO; - levelMapping[levels.WARN] = LOG_WARNING; - levelMapping[levels.ERROR] = LOG_ERR; - levelMapping[levels.FATAL] = LOG_CRIT; - host = host || 'localhost'; port = port || 12201; hostname = hostname || require('os').hostname(); facility = facility || 'nodejs-server'; - layout = layout || layouts.patternLayout('%m'); + layout = layout || layouts.messagePassThroughLayout; var client = dgram.createSocket("udp4"); @@ -61,18 +59,6 @@ function gelfAppender (layout, host, port, hostname, facility) { return msg; } - function flushBuffer() { - while (logEventBuffer.length > 0) { - var message = preparePacket(logEventBuffer.shift()); - var packet = compress(new Buffer(JSON.stringify(message))); - if (packet.length > 8192) { - util.debug("Message packet length (" + packet.length + ") is larger than 8k. Not sending"); - } else { - sendPacket(packet); - } - } - } - function sendPacket(packet) { try { client.send(packet, 0, packet.length, port, host); @@ -80,8 +66,13 @@ function gelfAppender (layout, host, port, hostname, facility) { } return function(loggingEvent) { - logEventBuffer.push(loggingEvent); - flushBuffer(); + var message = preparePacket(loggingEvent); + var packet = compress(new Buffer(JSON.stringify(message))); + if (packet.length > 8192) { + util.debug("Message packet length (" + packet.length + ") is larger than 8k. Not sending"); + } else { + sendPacket(packet); + } }; } diff --git a/test/gelfAppender.js b/test/gelfAppender.js index 8257766..713f146 100644 --- a/test/gelfAppender.js +++ b/test/gelfAppender.js @@ -1,57 +1,138 @@ var vows = require('vows') , assert = require('assert') , sandbox = require('sandboxed-module') -, fakeDgram = { - socket: { - packetLength: 0, - close: function() { +, log4js = require('../lib/log4js') +, setupLogging = function(options, category, compressedLength) { + var fakeDgram = { + sent: false, + socket: { + packetLength: 0, + close: function() { + }, + send: function(pkt, offset, pktLength, port, host) { + fakeDgram.sent = true; + this.packet = pkt; + this.offset = offset; + this.packetLength = pktLength; + this.port = port; + this.host = host; + } }, - send: function(pkt, offset, pktLength, port, host) { - this.packet = pkt; - this.offset = offset; - this.packetLength = pktLength; - this.port = port; - this.host = host; + createSocket: function(type) { + this.type = type; + return this.socket; } - }, - createSocket: function(type) { - this.type = type; - return this.socket; - } -} -, fakeCompressBuffer = { - compress: function(objectToCompress) { - this.uncompressed = objectToCompress; - return "I've been compressed"; } -} -, appender = sandbox.require('../lib/appenders/gelf', { - requires: { + , fakeCompressBuffer = { + compress: function(objectToCompress) { + fakeCompressBuffer.uncompressed = objectToCompress; + if (compressedLength) { + return { length: compressedLength }; + } else { + return "I've been compressed"; + } + } + } + , appender = sandbox.require('../lib/appenders/gelf', { + requires: { + dgram: fakeDgram, + "compress-buffer": fakeCompressBuffer + } + }); + + log4js.clearAppenders(); + log4js.addAppender(appender.configure(options || {}), category || "gelf-test"); + return { dgram: fakeDgram, - "compress-buffer": fakeCompressBuffer - } -}) -, log4js = require('../lib/log4js'); + compress: fakeCompressBuffer, + logger: log4js.getLogger(category || "gelf-test") + }; +}; -log4js.clearAppenders(); -log4js.addAppender(appender.configure({}), "gelf-test"); +//log4js.configure({ doNotReplaceConsole: true }); vows.describe('log4js gelfAppender').addBatch({ 'with default gelfAppender settings': { topic: function() { - log4js.getLogger("gelf-test").info("This is a test"); - return fakeDgram; + var setup = setupLogging(); + setup.logger.info("This is a test"); + return setup; + }, + 'the dgram packet': { + topic: function(setup) { + return setup.dgram; + }, + 'should be sent via udp to the localhost gelf server': function(dgram) { + assert.equal(dgram.type, "udp4"); + assert.equal(dgram.socket.host, "localhost"); + assert.equal(dgram.socket.port, 12201); + assert.equal(dgram.socket.offset, 0); + assert.ok(dgram.socket.packetLength > 0, "Received blank message"); + }, + 'should be compressed': function(dgram) { + assert.equal(dgram.socket.packet, "I've been compressed"); + } + }, + 'the uncompressed log message': { + topic: function(setup) { + var message = JSON.parse(setup.compress.uncompressed); + return message; + }, + 'should be in the gelf format': function(message) { + assert.equal(message.version, '1.0'); + assert.equal(message.host, require('os').hostname()); + assert.equal(message.level, 6); //INFO + assert.equal(message.facility, 'nodejs-server'); + assert.equal(message.full_message, message.short_message); + assert.equal(message.full_message, 'This is a test'); + } + } + }, + 'with a message longer than 8k': { + topic: function() { + var setup = setupLogging(undefined, undefined, 10240); + setup.logger.info("Blah."); + return setup; + }, + 'the dgram packet': { + topic: function(setup) { + return setup.dgram; + }, + 'should not be sent': function(dgram) { + assert.equal(dgram.sent, false); + } + } + }, + 'with non-default options': { + topic: function() { + var setup = setupLogging({ + host: 'somewhere', + port: 12345, + hostname: 'cheese', + facility: 'nonsense' + }); + setup.logger.debug("Just testing."); + return setup; }, - 'should send log messages via udp to the localhost gelf server': function(dgram) { - assert.equal(dgram.type, "udp4"); - assert.equal(dgram.socket.host, "localhost"); - assert.equal(dgram.socket.port, 12201); - assert.equal(dgram.socket.offset, 0); - assert.ok(dgram.socket.packetLength > 0, "Received blank message"); + 'the dgram packet': { + topic: function(setup) { + return setup.dgram; + }, + 'should pick up the options': function(dgram) { + assert.equal(dgram.socket.host, 'somewhere'); + assert.equal(dgram.socket.port, 12345); + } }, - 'should compress the log message': function(dgram) { - assert.equal(dgram.socket.packet, "I've been compressed"); + 'the uncompressed packet': { + topic: function(setup) { + var message = JSON.parse(setup.compress.uncompressed); + return message; + }, + 'should pick up the options': function(message) { + assert.equal(message.host, 'cheese'); + assert.equal(message.facility, 'nonsense'); + } } } }).export(module); \ No newline at end of file