Changed multiprocess appender to use a single socket per client

This commit is contained in:
Gareth Jones
2012-07-04 08:44:50 +10:00
parent df491c0b14
commit a33e48cb07
3 changed files with 140 additions and 83 deletions

View File

@@ -1,75 +1,133 @@
var log4js = require('../log4js'); var log4js = require('../log4js'),
var layouts = require('../layouts'); net = require('net'),
var net = require('net'); END_MSG = '__LOG4JS__';
var util = require('util');
var LogServer = function createLogServer(config) { /**
var actualAppender = config.actualAppender; * Creates a server, listening on config.loggerPort, config.loggerHost.
var server = net.createServer(function serverCreated(clientSocket) { * Output goes to config.actualAppender (config.appender is used to
clientSocket.on('connect', function clientConnected() { * set up that appender).
var logMessage = ''; */
clientSocket.on('data', function chunkReceived(chunk) { function logServer(config) {
logMessage += chunk;
}); /**
clientSocket.on('end', function gotChunks() { * Takes a utf-8 string, returns an object with
* the correct log properties.
*/
function deserializeLoggingEvent(clientSocket, msg) {
var loggingEvent;
try { try {
var loggingEvent = JSON.parse(logMessage); loggingEvent = JSON.parse(msg);
deserializeLoggingEvent(loggingEvent); loggingEvent.startTime = new Date(loggingEvent.startTime);
actualAppender(loggingEvent); loggingEvent.level.toString = function levelToString() {
return loggingEvent.level.levelStr;
};
} catch (e) { } catch (e) {
// JSON.parse failed, just log the contents probably a naughty. // JSON.parse failed, just log the contents probably a naughty.
actualAppender(createLoggingEvent('ERROR', 'Unable to parse log: ' + logMessage)); loggingEvent = {
startTime: new Date(),
categoryName: 'log4js',
level: { toString: function () {
return 'ERROR';
}
},
data: [ 'Unable to parse log:', msg ]
};
} }
});
});
});
server.listen(config.loggerPort || 5000, config.loggerHost || 'localhost');
}
function createLoggingEvent(level, message) { loggingEvent.remoteAddress = clientSocket.remoteAddress;
return { loggingEvent.remotePort = clientSocket.remotePort;
startTime: new Date(),
categoryName: 'log4js',
level: { toString: function () {
return level;
}},
data: [ message ]
};
}
function deserializeLoggingEvent(loggingEvent) { return loggingEvent;
loggingEvent.startTime = new Date(loggingEvent.startTime); }
loggingEvent.level.toString = function levelToString() {
return loggingEvent.level.levelStr; var actualAppender = config.actualAppender,
}; server = net.createServer(function serverCreated(clientSocket) {
clientSocket.setEncoding('utf8');
clientSocket.on('connect', function clientConnected() {
var logMessage = '';
function logTheMessage(msg) {
if (logMessage.length > 0) {
actualAppender(deserializeLoggingEvent(clientSocket, msg));
}
}
function chunkReceived(chunk) {
var event;
logMessage += chunk || '';
if (logMessage.indexOf(END_MSG) > -1) {
event = logMessage.substring(0, logMessage.indexOf(END_MSG));
logTheMessage(event);
logMessage = logMessage.substring(event.length + END_MSG.length) || '';
//check for more, maybe it was a big chunk
chunkReceived();
}
}
clientSocket.on('data', chunkReceived);
clientSocket.on('end', chunkReceived);
});
});
server.listen(config.loggerPort || 5000, config.loggerHost || 'localhost');
return actualAppender;
} }
function workerAppender(config) { function workerAppender(config) {
return function log(loggingEvent) { var canWrite = false,
var socket = net.createConnection(config.loggerPort || 5000, config.loggerHost || 'localhost'); buffer = [],
socket.on('connect', function socketConnected() { socket;
socket.end(JSON.stringify(loggingEvent), 'utf8');
}); createSocket();
};
function createSocket() {
socket = net.createConnection(config.loggerPort || 5000, config.loggerHost || 'localhost');
socket.on('connect', function() {
emptyBuffer();
canWrite = true;
});
socket.on('timeout', socket.end.bind(socket));
//don't bother listening for 'error', 'close' gets called after that anyway
socket.on('close', createSocket);
}
function emptyBuffer() {
var evt;
while ((evt = buffer.shift())) {
write(evt);
}
}
function write(loggingEvent) {
socket.write(JSON.stringify(loggingEvent), 'utf8');
socket.write(END_MSG, 'utf8');
}
return function log(loggingEvent) {
if (canWrite) {
write(loggingEvent);
} else {
buffer.push(loggingEvent);
}
};
} }
function createAppender(config) { function createAppender(config) {
if (config.mode === 'master') { if (config.mode === 'master') {
var server = new LogServer(config); return logServer(config);
return config.actualAppender; } else {
} else { return workerAppender(config);
return workerAppender(config); }
}
} }
function configure(config) { function configure(config) {
var actualAppender; var actualAppender;
if (config.appender && config.mode === 'master') { if (config.appender && config.mode === 'master') {
log4js.loadAppender(config.appender.type); log4js.loadAppender(config.appender.type);
actualAppender = log4js.appenderMakers[config.appender.type](config.appender); actualAppender = log4js.appenderMakers[config.appender.type](config.appender);
config.actualAppender = actualAppender; config.actualAppender = actualAppender;
} }
return createAppender(config); return createAppender(config);
} }
exports.appender = createAppender; exports.appender = createAppender;

View File

@@ -1,32 +1,32 @@
var dateFormat = require('./date_format') var dateFormat = require('./date_format')
, util = require('util') , util = require('util')
, replacementRegExp = /%[sdj]/g , replacementRegExp = /%[sdj]/g
, layoutMakers = { , layoutMakers = {
"messagePassThrough": function() { return messagePassThroughLayout; } "messagePassThrough": function() { return messagePassThroughLayout; }
, "basic": function() { return basicLayout; } , "basic": function() { return basicLayout; }
, "colored": function() { return colouredLayout; } , "colored": function() { return colouredLayout; }
, "coloured": function() { return colouredLayout; } , "coloured": function() { return colouredLayout; }
, "pattern": function (config) { , "pattern": function (config) {
var pattern = config.pattern || undefined; var pattern = config.pattern || undefined;
return patternLayout(pattern); return patternLayout(pattern);
} }
} }
, colours = { , colours = {
ALL: "grey" ALL: "grey"
, TRACE: "blue" , TRACE: "blue"
, DEBUG: "cyan" , DEBUG: "cyan"
, INFO: "green" , INFO: "green"
, WARN: "yellow" , WARN: "yellow"
, ERROR: "red" , ERROR: "red"
, FATAL: "magenta" , FATAL: "magenta"
, OFF: "grey" , OFF: "grey"
}; };
function formatLogData(logData) { function formatLogData(logData) {
var output = "" var output = ""
, data = Array.isArray(logData) ? logData.slice() : Array.prototype.slice.call(arguments) , data = Array.isArray(logData) ? logData.slice() : Array.prototype.slice.call(arguments)
, format = data.shift(); , format = data.shift();
if (typeof format === "string") { if (typeof format === "string") {
output = format.replace(replacementRegExp, function(match) { output = format.replace(replacementRegExp, function(match) {
@@ -257,4 +257,4 @@ module.exports = {
, layout: function(name, config) { , layout: function(name, config) {
return layoutMakers[name] && layoutMakers[name](config); return layoutMakers[name] && layoutMakers[name](config);
} }
}; };

View File

@@ -576,5 +576,4 @@ vows.describe('log4js').addBatch({
assert.equal(logEvents[1].data[0], 'info3'); assert.equal(logEvents[1].data[0], 'info3');
} }
} }
}).export(module); }).export(module);