RollingFileStream.js
3.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
"use strict";
var BaseRollingFileStream = require('./BaseRollingFileStream')
, debug = require('debug')('streamroller:RollingFileStream')
, util = require('util')
, path = require('path')
, child_process = require('child_process')
, fs = require('fs');
module.exports = RollingFileStream;
function RollingFileStream (filename, size, backups, options) {
//if you don't specify a size, this will behave like a normal file stream
this.size = size || Number.MAX_SAFE_INTEGER;
this.backups = backups || 1;
function throwErrorIfArgumentsAreNotValid() {
if (!filename || size <= 0) {
throw new Error("You must specify a filename and file size");
}
}
throwErrorIfArgumentsAreNotValid();
RollingFileStream.super_.call(this, filename, options);
}
util.inherits(RollingFileStream, BaseRollingFileStream);
RollingFileStream.prototype.shouldRoll = function() {
debug("should roll with current size ", this.currentSize, " and max size ", this.size);
return this.currentSize >= this.size;
};
RollingFileStream.prototype.roll = function(filename, callback) {
var that = this;
var fileNameObj = path.parse(filename);
var dir = fileNameObj.dir;
var name = fileNameObj.name;
var ext = fileNameObj.ext === '' ? '' : fileNameObj.ext.substring(1);
var nameMatcher = new RegExp('^' + name);
function justTheseFiles (item) {
return nameMatcher.test(item);
}
function getExtensions(filename_) {
return filename_.substring((name + '.').length).split('.');
}
function index(filename_) {
debug('Calculating index of '+filename_);
var exts = getExtensions(filename_);
if (exts[exts.length - 1] === 'gz') {
exts.pop();
}
if (that.options.keepFileExt) {
return parseInt(exts[0], 10) || 0;
} else {
return parseInt(exts[exts.length - 1]) || 0;
}
}
function byIndex(a, b) {
if (index(a) > index(b)) {
return 1;
} else if (index(a) < index(b) ) {
return -1;
} else {
return 0;
}
}
function increaseFileIndex (fileToRename, cb) {
var idx = index(fileToRename);
debug('Index of ' + fileToRename + ' is ' + idx);
if (idx < that.backups) {
var newIdx = (idx + 1).toString();
var fileNameItems = [name];
if (ext) {
if (that.options.keepFileExt) {
fileNameItems.push(newIdx, ext);
} else {
fileNameItems.push(ext, newIdx);
}
} else {
fileNameItems.push(newIdx);
}
var destination = path.join(dir, fileNameItems.join('.'));
if (that.options.compress && path.extname(fileToRename) === '.gz') {
destination += '.gz';
}
//on windows, you can get a EEXIST error if you rename a file to an existing file
//so, we'll try to delete the file we're renaming to first
fs.unlink(destination, function (err) {
//ignore err: if we could not delete, it's most likely that it doesn't exist
debug('Renaming ' + fileToRename + ' -> ' + destination);
fs.rename(path.join(dir, fileToRename), destination, function(err) {
if (err) {
cb(err);
} else {
if (that.options.compress && path.extname(fileToRename) !== '.gz') {
that.compress(destination, cb);
} else {
cb();
}
}
});
});
} else {
cb();
}
}
function renameTheFiles(cb) {
//roll the backups (rename file.n to file.n+1, where n <= numBackups)
debug("Renaming the old files");
fs.readdir(path.dirname(filename), function (err, files) {
if (err) {
return cb(err);
}
var filesToProcess = files.filter(justTheseFiles).sort(byIndex);
(function processOne(err) {
var file = filesToProcess.pop();
if (!file || err) { return cb(err); }
increaseFileIndex(file, processOne);
})();
});
}
debug("Rolling, rolling, rolling");
this.closeTheStream(
renameTheFiles.bind(null,
this.openTheStream.bind(this,
callback)));
};