diff --git a/lib/log4js.js b/lib/log4js.js index 9efd0a8..1cbac33 100644 --- a/lib/log4js.js +++ b/lib/log4js.js @@ -286,61 +286,106 @@ function consoleAppender (layout) { */ function fileAppender (file, layout, logSize, numBackups, filePollInterval) { layout = layout || layouts.basicLayout; - var logFile = fs.createWriteStream(file, { flags: 'a', mode: 0644, encoding: 'utf8' }); + numBackups = numBackups || 5; + filePollInterval = filePollInterval * 1000 || 30000; + + function setupLogRolling () { + fs.watchFile( + file, + { + persistent: false, + interval: filePollInterval + }, + function (curr, prev) { + if (curr.size >= logSize) { + rollThatLog(); + } + } + ); + } + + function rollThatLog () { + //roll the backups (rename file.n-1 to file.n, where n <= numBackups) + for (var i=numBackups; i > 0; i--) { + if (i > 1) { + if (fileExists(file + '.' + (i-1))) { + console.debug("Renaming " + file + '.' + (i-1) + ' -> ' + file + '.' + i); + fs.renameSync(file+'.'+(i-1), file+'.'+i); + } + } else { + console.debug("Renaming " + file + " -> " + file + ".1"); + fs.renameSync(file, file+'.1'); + } + } + //let's make a new file + var newLogFileFD = fs.openSync(file, 'a', 0644) + , oldLogFileFD = logFile.fd; + console.debug("made a new logFile ", newLogFileFD); + logFile.fd = newLogFileFD; + console.debug("closing old log file fd ", oldLogFileFD); + fs.close(oldLogFileFD); + } + + function fileExists (filename) { + try { + fs.statSync(filename); + return true; + } catch (e) { + return false; + } + } + + function openTheStream() { + console.debug("Creating the write Stream with file ", file); + var stream = fs.createWriteStream(file, { flags: 'a', mode: 0644, encoding: 'utf8' }); + stream.on("open", function() { + canWrite = true; + console.debug("Stream is open, writing %d buffered events", logEventBuffer.length); + while (logEventBuffer.length > 0 && canWrite) { + canWrite = writeToLog(logEventBuffer.shift()); + } + }); + stream.on("error", function (err) { + console.error("Error happened ", err); + }); + stream.on("drain", function() { + canWrite = true; + while (logEventBuffer.length > 0 && canWrite) { + canWrite = writeToLog(logEventBuffer.shift()); + } + }); + return stream; + } + + + var logEventBuffer = [] + , canWrite = false + , logFile = openTheStream(); if (logSize > 0) { - setupLogRolling(logFile, file, logSize, numBackups || 5, (filePollInterval * 1000) || 30000); + setupLogRolling(); } //close the file on process exit. process.on('exit', function() { + console.debug("Buffer contains %d events", logEventBuffer.length); logFile.end(); + logFile.destroy(); }); + function writeToLog(loggingEvent) { + return logFile.write(layout(loggingEvent)+'\n', "utf8"); + } + return function(loggingEvent) { - logFile.write(layout(loggingEvent)+'\n'); - }; -} - -function setupLogRolling (logFile, filename, logSize, numBackups, filePollInterval) { - fs.watchFile( - filename, - { - persistent: false, - interval: filePollInterval - }, - function (curr, prev) { - if (curr.size >= logSize) { - rollThatLog(logFile, filename, numBackups); - } - } - ); -} - -function rollThatLog (logFile, filename, numBackups) { - //first close the current one. - logFile.end(); - //roll the backups (rename file.n-1 to file.n, where n <= numBackups) - for (var i=numBackups; i > 0; i--) { - if (i > 1) { - if (fileExists(filename + '.' + (i-1))) { - fs.renameSync(filename+'.'+(i-1), filename+'.'+i); - } + //because the log stream is opened asynchronously, we don't want to write + //until it is ready. + if (canWrite) { + canWrite = writeToLog(loggingEvent); } else { - fs.renameSync(filename, filename+'.1'); + logEventBuffer.push(loggingEvent); } - } - //open it up again - logFile = fs.createWriteStream(filename, { flags: 'a', mode: 0644, encoding: "utf8" }); -} - -function fileExists (filename) { - try { - fs.statSync(filename); - return true; - } catch (e) { - return false; - } + }; } function configure (configurationFileOrObject) {