Skip to content

Commit 5868b78

Browse files
nimrod-beckerindexzero
authored andcommitted
Add support for archiving old log files
When working with file transport, add support for archiving (zip) the previous files.
1 parent b6ec262 commit 5868b78

File tree

2 files changed

+124
-8
lines changed

2 files changed

+124
-8
lines changed

lib/winston/transports/file.js

+41-8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ var events = require('events'),
1111
path = require('path'),
1212
util = require('util'),
1313
async = require('async'),
14+
zlib = require('zlib'),
1415
common = require('../common'),
1516
Transport = require('./transport').Transport,
1617
isWritable = require('isstream').isWritable,
@@ -75,6 +76,7 @@ var File = exports.File = function (options) {
7576
this.colorize = options.colorize || false;
7677
this.maxsize = options.maxsize || null;
7778
this.rotationFormat = options.rotationFormat || false;
79+
this.zippedArchive = options.zippedArchive || false;
7880
this.maxFiles = options.maxFiles || null;
7981
this.prettyPrint = options.prettyPrint || false;
8082
this.label = options.label || null;
@@ -100,6 +102,7 @@ var File = exports.File = function (options) {
100102
this._draining = false;
101103
this._opening = false;
102104
this._failures = 0;
105+
this._archive = null;
103106
};
104107

105108
//
@@ -371,6 +374,8 @@ File.prototype.open = function (callback) {
371374
return this._createStream();
372375
}
373376

377+
this._archive = this.zippedArchive ? this._stream.path : null;
378+
374379
//
375380
// Otherwise we have a valid (and ready) stream.
376381
//
@@ -496,22 +501,35 @@ File.prototype._createStream = function () {
496501
self.opening = false;
497502
self.emit('open', fullname);
498503
});
499-
500504
//
501505
// Remark: It is possible that in the time it has taken to find the
502506
// next logfile to be written more data than `maxsize` has been buffered,
503507
// but for sensible limits (10s - 100s of MB) this seems unlikely in less
504508
// than one second.
505509
//
506510
self.flush();
511+
compressFile();
512+
}
513+
514+
function compressFile() {
515+
if (self._archive) {
516+
var gzip = zlib.createGzip();
517+
518+
var inp = fs.createReadStream(String(self._archive));
519+
var out = fs.createWriteStream(self._archive + '.gz');
520+
521+
inp.pipe(gzip).pipe(out);
522+
523+
fs.unlink(String(self._archive));
524+
self._archive = '';
525+
}
507526
}
508527

509528
fs.stat(fullname, function (err, stats) {
510529
if (err) {
511530
if (err.code !== 'ENOENT') {
512531
return self.emit('error', err);
513532
}
514-
515533
return createAndFlush(0);
516534
}
517535

@@ -530,6 +548,7 @@ File.prototype._createStream = function () {
530548
})(this._getFile());
531549
};
532550

551+
533552
File.prototype._incFile = function (callback) {
534553
var ext = path.extname(this._basename),
535554
basename = path.basename(this._basename, ext),
@@ -570,15 +589,24 @@ File.prototype._getFile = function () {
570589
// checked by this instance.
571590
//
572591
File.prototype._checkMaxFilesIncrementing = function (ext, basename, callback) {
573-
var oldest, target;
592+
var oldest, target,
593+
self = this;
594+
595+
if (self.zippedArchive) {
596+
self._archive = path.join(self.dirname, basename +
597+
((self._created === 1) ? '' : self._created-1) +
598+
ext);
599+
}
600+
574601

575602
// Check for maxFiles option and delete file
576-
if (!this.maxFiles || this._created < this.maxFiles) {
603+
if (!self.maxFiles || self._created < self.maxFiles) {
577604
return callback();
578605
}
579606

580-
oldest = this._created - this.maxFiles;
581-
target = path.join(this.dirname, basename + (oldest !== 0 ? oldest : '') + ext);
607+
oldest = self._created - self.maxFiles;
608+
target = path.join(self.dirname, basename + (oldest !== 0 ? oldest : '') + ext +
609+
(self.zippedArchive ? '.gz' : ''));
582610
fs.unlink(target, callback);
583611
};
584612

@@ -600,18 +628,23 @@ File.prototype._checkMaxFilesTailable = function (ext, basename, callback) {
600628
for (var x = this.maxFiles - 1; x > 0; x--) {
601629
tasks.push(function (i) {
602630
return function (cb) {
603-
var tmppath = path.join(self.dirname, basename + (i - 1) + ext);
631+
var tmppath = path.join(self.dirname, basename + (i - 1) + ext +
632+
(self.zippedArchive ? '.gz' : ''));
604633
fs.exists(tmppath, function (exists) {
605634
if (!exists) {
606635
return cb(null);
607636
}
608637

609-
fs.rename(tmppath, path.join(self.dirname, basename + i + ext), cb);
638+
fs.rename(tmppath, path.join(self.dirname, basename + i + ext +
639+
(self.zippedArchive ? '.gz' : '')), cb);
610640
});
611641
};
612642
}(x));
613643
}
614644

645+
if (self.zippedArchive) {
646+
self._archive = path.join(self.dirname, basename + 1 + ext);
647+
}
615648
async.series(tasks, function (err) {
616649
fs.rename(
617650
path.join(self.dirname, basename + ext),

test/transports/file-archive-test.js

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* file-archive-test.js: Tests for instances of the File transport setting the archive option,
3+
*
4+
* (C) 2015 Nimrod Becker
5+
* MIT LICENSE
6+
*
7+
*/
8+
9+
var assert = require('assert'),
10+
exec = require('child_process').exec,
11+
fs = require('fs'),
12+
path = require('path'),
13+
vows = require('vows'),
14+
winston = require('../../lib/winston'),
15+
helpers = require('../helpers');
16+
17+
var archiveTransport = new winston.transports.File({
18+
timestamp: true,
19+
json: false,
20+
zippedArchive: true,
21+
tailable: true,
22+
filename: 'testarchive.log',
23+
dirname: path.join(__dirname, '..', 'fixtures', 'logs'),
24+
maxsize: 4096,
25+
maxFiles: 3
26+
});
27+
28+
function data(ch) {
29+
return new Array(1018).join(String.fromCharCode(65 + ch));
30+
}
31+
32+
function logKbytes(kbytes, txt) {
33+
//
34+
// With no timestamp and at the info level,
35+
// winston adds exactly 7 characters:
36+
// [info](4)[ :](2)[\n](1)
37+
//
38+
for (var i = 0; i < kbytes; i++) {
39+
archiveTransport.log('info', data(txt), null, function() {});
40+
}
41+
}
42+
43+
vows.describe('winston/transports/file/zippedArchive').addBatch({
44+
"An instance of the File Transport with tailable true": {
45+
"when created archived files are rolled": {
46+
topic: function() {
47+
var that = this,
48+
created = 0;
49+
50+
archiveTransport.on('logged', function() {
51+
if (++created === 6) {
52+
return that.callback();
53+
}
54+
55+
logKbytes(4, created);
56+
});
57+
58+
logKbytes(4, created);
59+
},
60+
"should be only 3 files called testarchive.log, testarchive1.log.gz and testarchive2.log.gz": function() {
61+
//Give the archive a little time to settle
62+
// setTimeout(function() {
63+
for (var num = 0; num < 6; num++) {
64+
var file = !num ? 'testarchive.log' : 'testarchive' + num + '.log.gz',
65+
fullpath = path.join(__dirname, '..', 'fixtures', 'logs', file);
66+
67+
// There should be no files with that name
68+
if (num >= 3) {
69+
assert.throws(function() {
70+
fs.statSync(fullpath);
71+
}, Error);
72+
} else {
73+
// The other files should exist
74+
assert.doesNotThrow(function() {
75+
fs.statSync(fullpath);
76+
}, Error);
77+
}
78+
}
79+
//},5000);
80+
},
81+
}
82+
},
83+
}).export(module);

0 commit comments

Comments
 (0)