rotating-file-stream
Advanced tools
Comparing version 0.0.2 to 0.0.3
226
index.js
"use strict"; | ||
var fs = require("fs"); | ||
var fs = require("fs"); | ||
var util = require("util"); | ||
var utils = require("./utils"); | ||
var Writable = require("stream").Writable; | ||
var RotatingFileStream = require("./constructor"); | ||
function RotatingFileStream(filename, options) { | ||
if(! (this instanceof RotatingFileStream)) | ||
return new RotatingFileStream(filename, options); | ||
function unexpected(msg) { | ||
throw new Error("Unexpected case ( https://www.npmjs.com/package/rotating-file-stream#unexpected ): " + msg); | ||
if(typeof filename == "function") | ||
this.generator = filename; | ||
else | ||
if(typeof filename == "string") | ||
this.generator = utils.createGenerator(filename); | ||
else | ||
throw new Error("Don't know how to handle 'filename' type: " + typeof filename); | ||
if(! options) | ||
options = {}; | ||
else | ||
if(typeof options != "object") | ||
throw new Error("Don't know how to handle 'options' type: " + typeof options); | ||
utils.checkOptions(options); | ||
Writable.call(this, options.highWaterMark ? { highWaterMark: options.highWaterMark } : {} ); | ||
this.options = options; | ||
this.size = 0; | ||
utils.setEvents(this); | ||
this.firstOpen(); | ||
} | ||
RotatingFileStream.prototype._write = function(chunk, encoding, callback) { | ||
if(this.err) | ||
unexpected("_write after error"); | ||
util.inherits(RotatingFileStream, Writable); | ||
if(this.callback) | ||
unexpected("_write before callback"); | ||
RotatingFileStream.prototype._callback = function(err) { | ||
if(! this.callback) { | ||
if(err) | ||
process.nextTick(this.emit.bind(this, "error", err)); | ||
if(! this.stream) { | ||
this.buffer += chunk; | ||
this.callback = callback; | ||
return; | ||
} | ||
var self = this; | ||
if(err) | ||
process.nextTick(this.callback.bind(null, err)); | ||
else | ||
process.nextTick(this._rewrite.bind(this, this.chunks, this.index, this.callback)); | ||
this.size += chunk.length; | ||
this.stream.write(chunk, function(err) { | ||
if(err) | ||
return callback(err); | ||
this.callback = null; | ||
}; | ||
if(self.options.size && self.size >= self.options.size) | ||
return self.rotate(callback); | ||
RotatingFileStream.prototype._postrewrite = function(chunks, index, callback, next) { | ||
this.callback = callback; | ||
this.chunks = chunks; | ||
this.index = index; | ||
callback(); | ||
}); | ||
if(next) | ||
next(); | ||
}; | ||
RotatingFileStream.prototype._writev = function(chunks, callback) { | ||
if(this.err) | ||
unexpected("_writev after error"); | ||
RotatingFileStream.prototype._rewrite = function(chunks, index, callback, err) { | ||
if(err) | ||
return callback(err); | ||
if(this.callback) | ||
unexpected("_writev before callback"); | ||
if(! this.stream) | ||
unexpected("_writev while initial rotation"); | ||
return this._postrewrite(chunks, index, callback); | ||
var buffer = ""; | ||
var enough = true; | ||
var i; | ||
var self = this; | ||
if(this.options.size && this.size >= this.options.size) | ||
return this._postrewrite(chunks, index, callback, this.rotate.bind(this)); | ||
for(i = 0; i < chunks.length && enough; ++i) { | ||
buffer += chunks[i].chunk; | ||
if(chunks.length == index) | ||
return callback(); | ||
if(this.options.size && (buffer.length + this.size >= this.options.size)) | ||
enough = false; | ||
var buffer = new Buffer(0); | ||
var chunk; | ||
if(index + 1 == chunks.length) { | ||
chunk = chunks[index++].chunk; | ||
this.size += chunk.length; | ||
buffer = chunk; | ||
} | ||
else | ||
while(index < chunks.length && ((! this.options.size) || this.size < this.options.size)) { | ||
chunk = chunks[index++].chunk; | ||
this.size += chunk.length; | ||
buffer = Buffer.concat([buffer, chunk], buffer.length + chunk.length); | ||
} | ||
this.size += buffer.length; | ||
this.stream.write(buffer, function(err) { | ||
if(err) | ||
return self.error(err, callback); | ||
this.stream.write(buffer, this._rewrite.bind(this, chunks, index, callback)); | ||
}; | ||
if(enough) | ||
return callback(); | ||
for(0; i < chunks.length; ++i) | ||
self.buffer += chunks[i].chunk; | ||
self.rotate(callback); | ||
}); | ||
RotatingFileStream.prototype._write = function(chunk, encoding, callback) { | ||
this._rewrite([{ chunk: chunk }], 0, callback); | ||
}; | ||
RotatingFileStream.prototype.error = function(err, callback) { | ||
if(this.callback) | ||
callback = this.callback; | ||
this.callback = null; | ||
if(callback) | ||
return callback(err); | ||
this.emit("error", err); | ||
RotatingFileStream.prototype._writev = function(chunks, callback) { | ||
this._rewrite(chunks, 0, callback); | ||
}; | ||
@@ -130,2 +145,48 @@ | ||
RotatingFileStream.prototype._interval = function(now) { | ||
now = new Date(now); | ||
var year = now.getFullYear(); | ||
var month = now.getMonth(); | ||
var day = now.getDate(); | ||
var hours = now.getHours(); | ||
var num = this.options.interval.num; | ||
var unit = this.options.interval.unit; | ||
if(unit == "d") | ||
hours = 0; | ||
else | ||
hours = parseInt(hours / num) * num; | ||
this.prev = new Date(year, month, day, hours, 0, 0, 0).getTime(); | ||
if(unit == "d") | ||
this.next = new Date(year, month, day + num, hours, 0, 0, 0).getTime(); | ||
else | ||
this.next = new Date(year, month, day, hours + num, 0, 0, 0).getTime(); | ||
}; | ||
RotatingFileStream.prototype.interval = function() { | ||
if(! this.options.interval) | ||
return; | ||
var now = new Date().getTime(); | ||
var unit = this.options.interval.unit; | ||
if(unit == "d" || unit == "h") { | ||
this._interval(now); | ||
} | ||
else { | ||
var period = 1000 * this.options.interval.num; | ||
if(unit == "m") | ||
period *= 60; | ||
this.prev = parseInt(now / period) * period; | ||
this.next = this.prev + period; | ||
} | ||
this.timer = setTimeout(this.rotate.bind(this), this.next - now); | ||
this.timer.unref(); | ||
}; | ||
RotatingFileStream.prototype.move = function(attempts) { | ||
@@ -145,9 +206,6 @@ if(! attempts) | ||
return this.error(err, this.callback); | ||
return this._callback(err); | ||
} | ||
if(this.options.interval) | ||
throw new Error("not implemented yet"); | ||
var name = this.generator(this.rotation, count + 1); | ||
var name = this.generator(this.options.interval ? new Date(this.prev) : this.rotation, count + 1); | ||
var self = this; | ||
@@ -170,6 +228,7 @@ | ||
if(err) | ||
return self.error(err, this.callback); | ||
return self._callback(err); | ||
self.emit("rotated", name); | ||
self.open(); | ||
self.size = 0; | ||
}); | ||
@@ -184,12 +243,6 @@ }); | ||
var callback = function(err) { | ||
var cb = self.callback; | ||
self._callback(err); | ||
if(cb) { | ||
self.callback = null; | ||
return cb(err); | ||
} | ||
if(err) | ||
throw err; | ||
if(! err) | ||
self.interval(); | ||
}; | ||
@@ -209,23 +262,14 @@ | ||
if(! this.buffer.length) | ||
return callback(); | ||
this.stream.write(this.buffer, function(err) { | ||
if(err) | ||
return self.error(err, callback); | ||
callback(); | ||
}); | ||
this.size += this.buffer.length; | ||
this.buffer = ""; | ||
callback(); | ||
}; | ||
RotatingFileStream.prototype.rotate = function(callback) { | ||
if(callback) | ||
this.callback = callback; | ||
RotatingFileStream.prototype.rotate = function() { | ||
if(this.timer) { | ||
clearTimeout(this.timer); | ||
this.timer = null; | ||
} | ||
this.size = 0; | ||
this.size = 0; | ||
this.rotation = new Date(); | ||
this.emit("rotation"); | ||
process.nextTick(this.emit.bind(this, "rotation")); | ||
@@ -232,0 +276,0 @@ if(this.stream) { |
{ | ||
"name": "rotating-file-stream", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "Opens a stream.Writable to a file rotated by interval and/or size. A logrotate alternative.", | ||
"scripts": { | ||
"test": "rm -rf *log ; echo '.jshintrc\\n.gitignore\\n.travis.yml\\ntest\\nsleep.js' > .npmignore ; cat .gitignore >> .npmignore\n ./node_modules/.bin/jshint index.js test || exit 1\n ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --recursive test" | ||
"coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --recursive test", | ||
"jshint": "./node_modules/.bin/jshint *.js test", | ||
"npmignore": "echo '.jshintrc\\n.gitignore\\n.travis.yml\\ntest\\nsleep.js' > .npmignore ; cat .gitignore >> .npmignore", | ||
"t": "./node_modules/.bin/_mocha test", | ||
"test": "npm run npmignore && npm run jshint && npm run coverage" | ||
}, | ||
@@ -16,10 +20,13 @@ "bugs": "https://github.com/iccicci/rotating-file-stream/issues", | ||
"author": "Daniele Ricci <daniele.icc@gmail.com> (https://github.com/iccicci)", | ||
"contributors": [ | ||
"allevo" | ||
], | ||
"license": "MIT", | ||
"readmeFilename": "README.md", | ||
"devDependencies": { | ||
"istanbul": "0.3.19", | ||
"istanbul": "0.3.21", | ||
"jshint": "2.8.0", | ||
"mocha": "2.3.2", | ||
"mocha": "2.3.3", | ||
"mocha-istanbul": "0.2.0" | ||
} | ||
} |
@@ -62,3 +62,3 @@ # rotating-file-stream | ||
function pad(num) { | ||
return (num + "").length == 1 ? "0" + num : num; | ||
return (num > 9 ? "" : "0") + num; | ||
} | ||
@@ -107,2 +107,7 @@ | ||
```javascript | ||
size: '300B', // rotates the file when its size exceeds 300 Bytes | ||
// useful for tests | ||
``` | ||
```javascript | ||
size: '300K', // rotates the file when its size exceeds 300 KiloBytes | ||
@@ -116,3 +121,3 @@ ``` | ||
```javascript | ||
size: '1G', // rotates the file when its size exceeds a GigaBytes | ||
size: '1G', // rotates the file when its size exceeds a GigaByte | ||
``` | ||
@@ -130,2 +135,7 @@ | ||
```javascript | ||
interval: '5s', // rotates the file at seconds 0, 5, 10, 15 and so on | ||
// useful for tests | ||
``` | ||
```javascript | ||
interval: '5m', // rotates the file at minutes 0, 5, 10, 15 and so on | ||
@@ -201,2 +211,16 @@ ``` | ||
### Rotation logic | ||
Regardless of when and why rotation happens, the content of a single __stream.write__ will never be | ||
split among two files. | ||
#### by size | ||
Once the no rotated file is opened first time, its size is checked and if it is greater or equal to | ||
size limit, a first rotation happens. After each __stream.write__, the same check is performed. | ||
#### by interval | ||
The package sets a _Timeout_ to start a rotation job at the right moment. | ||
### Under the hood | ||
@@ -215,14 +239,4 @@ | ||
### Unexpected | ||
Once an __error__ _event_ is emitted, nothing more can be done: the stream is closed as well. | ||
``` | ||
If I understood correctly, there are some case which should never happen. | ||
Anyway I want to be sure, so I decided to throw an Error if code runs | ||
through one of these cases. | ||
If it happen that you catch one of these, please make me aware of that as | ||
soon as possible in order to handle the case. | ||
The author | ||
``` | ||
### Compatibility | ||
@@ -232,2 +246,3 @@ | ||
compatibility. The package it tested under following versions: | ||
* 4.1 | ||
* 4.0 | ||
@@ -248,11 +263,13 @@ * 0.12 | ||
* Rotate by interval | ||
* Create missing directories in paths | ||
* External compression | ||
* Internal compression gzip | ||
* Internal compression bzip | ||
* Create missing directories in paths | ||
* Test all error case handling | ||
### Changelog | ||
### ChangeLog | ||
* 2015-09-29 - v0.0.3 | ||
* Rotation by interval | ||
* __Buffer__ optimization (thanks to [allevo](https://www.npmjs.com/~allevo)) | ||
* 2015-09-17 - v0.0.2 | ||
@@ -259,0 +276,0 @@ * Rotation by size |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
20725
343
273