Skip to content

Commit

Permalink
added tailable option to file transport which rolls files backwards i…
Browse files Browse the repository at this point in the history
…nstead of creating incrementing appends. implements #268
  • Loading branch information
nick.peeples@gmail.com authored and indexzero committed Jan 27, 2015
1 parent a34f7d2 commit 8b5fbcd
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 23 deletions.
77 changes: 54 additions & 23 deletions lib/winston/transports/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ var File = exports.File = function (options) {
this.label = options.label || null;
this.timestamp = options.timestamp != null ? options.timestamp : true;
this.eol = options.eol || '\n';
this.tailable = options.tailable || false;
this.depth = options.depth || null;
this.showLevel = options.showLevel === undefined ? true : options.showLevel;

Expand Down Expand Up @@ -505,39 +506,69 @@ File.prototype._createStream = function () {
// in the case that log filesizes are being capped.
//
File.prototype._getFile = function (inc) {
var self = this,
ext = path.extname(this._basename),
basename = path.basename(this._basename, ext),
remaining;
var ext = path.extname(this._basename),
basename = path.basename(this._basename, ext);

if (inc) {
//
// Increment the number of files created or
// checked by this instance.
//
// Check for maxFiles option and delete file
if (this.maxFiles && (this._created >= (this.maxFiles - 1))) {
remaining = this._created - (this.maxFiles - 1);
try {
if (remaining === 0) {
fs.unlinkSync(path.join(this.dirname, basename + ext));
}
else {
fs.unlinkSync(path.join(this.dirname, basename + remaining + ext));
}
} catch (e) {
// If the file was already removed
}
if (!this.tailable) {
this._checkMaxFilesIncrementing(ext, basename);
this._created += 1;
}
else {
this._checkMaxFilesTailable(ext, basename);
return basename + ext;
}

this._created += 1;
}

return this._created
? basename + (this.rotationFormat ? this.rotationFormat() : this._created) + ext
: basename + ext;
};

//
// ### @private function _checkMaxFilesIncrementing ()
// Increment the number of files created or
// checked by this instance.
//
File.prototype._checkMaxFilesIncrementing = function (ext, basename) {
var remaining;

// Check for maxFiles option and delete file
if (this.maxFiles && (this._created >= (this.maxFiles - 1))) {
remaining = this._created - (this.maxFiles - 1);
if (remaining === 0) {
fs.unlinkSync(path.join(this.dirname, basename + ext));
}
else {
fs.unlinkSync(path.join(this.dirname, basename + remaining + ext));
}
}
};

//
// ### @private function _checkMaxFilesTailable ()
//
// Roll files forward based on integer, up to maxFiles.
// e.g. if base if file.log and it becomes oversized, roll
// to file1.log, and allow file.log to be re-used. If
// file is oversized again, roll file1.log to file2.log,
// roll file.log to file1.log, and so on.
File.prototype._checkMaxFilesTailable = function (ext, basename) {
var tmppath;

if (!this.maxFiles)
return;

for (var x = this.maxFiles - 1; x > 0; x--) {
tmppath = path.join(this.dirname, basename + (x - 1) + ext);
if (fs.existsSync(tmppath)) {
fs.renameSync(tmppath, path.join(this.dirname, basename + x + ext));
}
}

fs.renameSync(path.join(this.dirname, basename + ext), path.join(this.dirname, basename + 1 + ext));
};

//
// ### @private function _lazyDrain ()
// Lazily attempts to emit the `logged` event when `this.stream` has
Expand Down
92 changes: 92 additions & 0 deletions test/transports/file-tailrolling-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
var assert = require('assert'),
fs = require('fs'),
path = require('path'),
vows = require('vows'),
winston = require('../../lib/winston'),
helpers = require('../helpers');

var maxfilesTransport = new winston.transports.File({
timestamp: false,
json: false,
filename: path.join(__dirname, '..', 'fixtures', 'logs', 'testtailrollingfiles.log'),
maxsize: 4096,
maxFiles: 3,
tailable: true
});

process.on('uncaughtException', function (err) {
console.log('caught exception');
console.log(err);
});

vows.describe('winston/transports/file/tailrolling').addBatch({
"An instance of the File Transport": {
"when delete old test files": {
topic: function () {
var logs = path.join(__dirname, '..', 'fixtures', 'logs');
fs.readdirSync(logs).forEach(function (file) {
if (~file.indexOf('testtailrollingfiles')) {
fs.unlinkSync(path.join(logs, file));
}
});

this.callback();
},
"and when passed more files than the maxFiles": {
topic: function () {
var that = this,
created = 0;

function data(ch) {
return new Array(1018).join(String.fromCharCode(65 + ch));
};

function logKbytes(kbytes, txt) {
//
// With no timestamp and at the info level,
// winston adds exactly 7 characters:
// [info](4)[ :](2)[\n](1)
//
for (var i = 0; i < kbytes; i++) {
maxfilesTransport.log('info', data(txt), null, function () { });
}
}

maxfilesTransport.on('logged', function () {
if (++created == 4) {
return that.callback();
}

logKbytes(4, created);
});

logKbytes(4, created);
},
"should be 3 log files, base to maxFiles - 1": function () {
var file, fullpath;
for (var num = 0; num < 4; num++) {
file = !num ? 'testtailrollingfiles.log' : 'testtailrollingfiles' + num + '.log';
fullpath = path.join(__dirname, '..', 'fixtures', 'logs', file);

if (num == 3) {
return assert.ok(!fs.existsSync(fullpath));
}

assert.ok(fs.existsSync(fullpath));
}

return false;
},
"should have files in correct order": function () {
var file, fullpath, content;
['D', 'C', 'B'].forEach(function (letter, i) {
file = !i ? 'testtailrollingfiles.log' : 'testtailrollingfiles' + i + '.log';
content = fs.readFileSync(path.join(__dirname, '..', 'fixtures', 'logs', file), 'ascii');

assert.lengthOf(content.match(new RegExp(letter, 'g')), 4068);
});
}
}
}
}
}).export(module);

0 comments on commit 8b5fbcd

Please sign in to comment.