log4js-node/lib/streams/RollingFileStream.js
e-cloud 9f581f78c1 refactor: streams/*.js (test passed)
As class inheritance inside stream/*.js, standalone refactor don't work.
So streams/*.js are refactored in batch.
One thing worth mentioning is that, the old inherit-style will call the parent
constructor at the end of child constructor for some code initialization. However,
with ES6 class syntax, the call of `super` must be afore every statement with this.
So, some code initializing logic are reordered to suit the ES6 syntax and the tests.
2016-07-16 13:20:01 +08:00

118 lines
3.4 KiB
JavaScript

'use strict';
const BaseRollingFileStream = require('./BaseRollingFileStream');
const debug = require('../debug')('RollingFileStream');
const path = require('path');
const zlib = require('zlib');
const fs = require('fs');
class RollingFileStream extends BaseRollingFileStream {
constructor(filename, size, backups, options) {
function throwErrorIfArgumentsAreNotValid() {
if (!filename || !size || size <= 0) {
throw new Error('You must specify a filename and file size');
}
}
throwErrorIfArgumentsAreNotValid();
super(filename, options);
this.size = size;
this.backups = backups || 1;
}
shouldRoll() {
debug(`should roll with current size ${this.currentSize} and max size ${this.size}`);
return this.currentSize >= this.size;
}
roll(filename, callback) {
const that = this;
const nameMatcher = new RegExp(`^${path.basename(filename)}`);
function justTheseFiles(item) {
return nameMatcher.test(item);
}
function index(filename_) {
debug(`Calculating index of ${filename_}`);
return parseInt(filename_.substring((`${path.basename(filename)}.`).length), 10) || 0;
}
function byIndex(a, b) {
if (index(a) > index(b)) {
return 1;
} else if (index(a) < index(b)) {
return -1;
}
return 0;
}
function increaseFileIndex(fileToRename, cb) {
const idx = index(fileToRename);
debug(`Index of ${fileToRename} is ${idx}`);
if (idx < that.backups) {
const ext = path.extname(fileToRename);
let destination = `${filename}.${idx + 1}`;
if (that.options.compress && /^gz$/.test(ext.substring(1))) {
destination += ext;
}
// 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
/* eslint no-unused-vars:0 */
fs.unlink(destination, 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(path.dirname(filename), fileToRename), destination, _err => {
if (_err) {
cb(_err);
} else {
if (that.options.compress && ext !== '.gz') {
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), (err, files) => {
const filesToProcess = files.filter(justTheseFiles).sort(byIndex);
(function processOne(_err) {
const file = filesToProcess.pop();
if (!file || _err) {
return cb(_err);
}
return increaseFileIndex(file, processOne);
}());
});
}
debug('Rolling, rolling, rolling');
this.closeTheStream(
renameTheFiles.bind(null,
this.openTheStream.bind(this, callback)));
}
}
function compress(filename, cb) {
const gzip = zlib.createGzip();
const inp = fs.createReadStream(filename);
const out = fs.createWriteStream(`${filename}.gz`);
inp.pipe(gzip).pipe(out);
fs.unlink(filename, cb);
}
module.exports = RollingFileStream;