Skip to content

Commit 0ef653b

Browse files
markjbyrne81mattberther
authored andcommitted
Enable the deletion of old log files after X log files have been created. (#50)
* Enable deletion of old files when maxsize option is not set This change enables the transport to delete old files when maxsize has not been configured. This functionality uses the maxfiles property to determine how many files are kept and will delete files that exceed this limit beginning with the oldest. During start up the transport will check the log directory for any files that contain the the _basename used by it and will save these filenames to an internal variable. These files will count towards the maxfiles limit. Example. The daily rotating file transport for the error level uses a MM-dd date pattern, prepends this pattern to the file name and has maxfiles set to 3. On the 2nd of Jan the transport starts and discovers a file already exists in the log directory for the 1st of Jan and proceeds to create a file for the 2nd of Jan e.g. 01-01.error.log 01-02.error.log While creating the log file for the 4th of Jan the transport realizes it has hit its max file limit and so deletes the 01-01.error.log file leaving the log directory with the following contents. 01-02.error.log 01-03.error.log 01-04.error.log This functionality will not be enabled if the maxsize property has been configured for the transport. * Update _currentFiles initialization function Update the function that initializes the internal _currentFiles variable to ensure the filenames are returned in chronological order. * fixing linting issues * Adding tests for new functionality Tests added to ensure file rotation and file deletion behaves as expected when maxsize is not set but maxfiles is. * Remove use of fs.accessSync Removed the uses of fs.accessSync as they are not available in older versions of Node and were breaking the Travis Integration tests.
1 parent 70d4861 commit 0ef653b

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

index.js

+48
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,34 @@ var DailyRotateFile = module.exports = function (options) {
9393
this._draining = false;
9494
this._failures = 0;
9595

96+
// Internal variable which will hold a record of all files
97+
// belonging to this transport which are currently in the
98+
// log directory in chronological order.
99+
//
100+
this._currentFiles = function () {
101+
//
102+
// Only proceed if maxsize is not configured for this transport.
103+
if (!this.maxsize) {
104+
try {
105+
return fs.readdirSync(this.dirname).filter(function (file) {
106+
return file.includes(this._basename);
107+
}.bind(this)).map(function (file) {
108+
return {
109+
name: file,
110+
time: fs.statSync(path.join(this.dirname, file)).mtime.getTime()
111+
};
112+
}.bind(this)).sort(function (a, b) {
113+
return a.time - b.time;
114+
}).map(function (v) {
115+
return v.name;
116+
});
117+
} catch (e) {
118+
// directory doesnt exist so there are no files. Do nothing.
119+
}
120+
}
121+
return [];
122+
}.bind(this)();
123+
96124
var now = new Date();
97125
this._year = now.getUTCFullYear();
98126
this._month = now.getUTCMonth();
@@ -598,6 +626,26 @@ DailyRotateFile.prototype._getFile = function (inc) {
598626
}
599627

600628
this._created += 1;
629+
} else if (!this.maxsize) {
630+
//
631+
// If the filename does not exist in the _currentFiles array then add it.
632+
if (this._currentFiles.indexOf(filename) === -1) {
633+
this._currentFiles.push(filename);
634+
}
635+
636+
// While the _currentFiles array contains more file names than is configured
637+
// in maxFiles loop the _currentFiles array and delete the file found at el
638+
// 0.
639+
while (this.maxFiles && (this._currentFiles.length > this.maxFiles)) {
640+
try {
641+
fs.unlinkSync(path.join(this.dirname, this._currentFiles[0]));
642+
} catch (e) {
643+
// File isn't accessible, do nothing.
644+
}
645+
646+
// Remove the filename that was just deleted from the _currentFiles array.
647+
this._currentFiles = this._currentFiles.slice(1);
648+
}
601649
}
602650

603651
return this._created ? filename + '.' + this._created : filename;

test/simple.tests.js

+80
Original file line numberDiff line numberDiff line change
@@ -401,5 +401,85 @@ describe('winston/transports/daily-rotate-file', function () {
401401
});
402402
});
403403
});
404+
405+
describe('when passed with maxfiles set and maxsize not set', function () {
406+
var dailyRotationPattern = {
407+
pattern: '.yyyy-MM-dd',
408+
jan1: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT
409+
jan2: 1862033560000, // GMT: Mon, 02 Jan 2029 07:32:40 GMT
410+
jan3: 1862119960000, // GMT: Mon, 03 Jan 2029 07:32:40 GMT
411+
file1: 'test-rotation-no-maxsize.log.2029-01-01',
412+
file2: 'test-rotation-no-maxsize.log.2029-01-02',
413+
file3: 'test-rotation-no-maxsize.log.2029-01-03'
414+
};
415+
416+
describe('when passed the pattern ' + dailyRotationPattern.pattern + ' and no maxsize', function () {
417+
var transport;
418+
var rotationLogPath = path.join(fixturesDir, 'rotations_no_maxsize');
419+
420+
beforeEach(function (done) {
421+
rimraf.sync(rotationLogPath);
422+
mkdirp.sync(rotationLogPath);
423+
transport = new DailyRotateFile({
424+
filename: path.join(rotationLogPath, 'test-rotation-no-maxsize.log'),
425+
datePattern: dailyRotationPattern.pattern,
426+
maxFiles: 2
427+
});
428+
429+
done();
430+
});
431+
432+
afterEach(function () {
433+
tk.reset();
434+
});
435+
436+
it('should properly rotate log without maxzsize set and with old files getting deleted', function (done) {
437+
var self = this;
438+
self.time = new Date(dailyRotationPattern.jan1);
439+
tk.travel(self.time);
440+
441+
transport.log('error', 'test message on Jan 1st', {}, function (err) {
442+
if (err) {
443+
done(err);
444+
}
445+
446+
self.time = new Date(dailyRotationPattern.jan2);
447+
tk.travel(self.time);
448+
449+
transport.log('error', 'test message on Jan 2nd', {}, function (err) {
450+
if (err) {
451+
done(err);
452+
}
453+
454+
self.time = new Date(dailyRotationPattern.jan3);
455+
tk.travel(self.time);
456+
457+
transport.log('error', 'test message on Jan 3rd', {}, function (err) {
458+
if (err) {
459+
done(err);
460+
}
461+
462+
self.time = new Date(dailyRotationPattern.jan3);
463+
tk.travel(self.time);
464+
465+
transport.log('error', 'second test message on Jan 3rd', {}, function (err) {
466+
if (err) {
467+
done(err);
468+
}
469+
470+
var filesCreated = fs.readdirSync(rotationLogPath);
471+
console.log('files : ' + filesCreated);
472+
expect(filesCreated.length).to.eql(2);
473+
expect(filesCreated).not.to.include(dailyRotationPattern.file1);
474+
expect(filesCreated).to.include(dailyRotationPattern.file2);
475+
expect(filesCreated).to.include(dailyRotationPattern.file3);
476+
done();
477+
});
478+
});
479+
});
480+
});
481+
});
482+
});
483+
});
404484
});
405485
});

0 commit comments

Comments
 (0)