diff --git a/lib/bunyan.js b/lib/bunyan.js index 24ff3bc1..6910c7dd 100644 --- a/lib/bunyan.js +++ b/lib/bunyan.js @@ -650,29 +650,26 @@ Logger.prototype.reopenFileStreams = function () { }; -/* BEGIN JSSTYLED */ /** * Close this logger. * * This closes streams (that it owns, as per 'endOnClose' attributes on * streams), etc. Typically you **don't** need to bother calling this. + */ Logger.prototype.close = function () { if (this._closed) { return; } if (!this._isSimpleChild) { - self.streams.forEach(function (s) { - if (s.endOnClose) { - xxx('closing stream s:', s); + this.streams.forEach(function (s) { + if (s.closeOnExit) { s.stream.end(); - s.endOnClose = false; + s.closeOnExit = false; } }); } this._closed = true; } - */ -/* END JSSTYLED */ /** @@ -1092,9 +1089,8 @@ if (mv) { function RotatingFileStream(options) { this.path = options.path; - this.stream = fs.createWriteStream(this.path, - {flags: 'a', encoding: 'utf8'}); this.count = (options.count == null ? 10 : options.count); + assert.equal(typeof (this.count), 'number', format('rotating-file stream "count" is not a number: %j (%s) in %j', this.count, typeof (this.count), this)); @@ -1102,6 +1098,9 @@ function RotatingFileStream(options) { format('rotating-file stream "count" is not >= 0: %j in %j', this.count, this)); + assert.ok((options.period && !options.size) || (!options.period && options.size), + format('period and size setting on rotating-file are mutually exclusive')); + // Parse `options.period`. if (options.period) { // where scope is: @@ -1125,6 +1124,12 @@ function RotatingFileStream(options) { } this.periodNum = Number(m[1]); this.periodScope = m[2]; + } else if (options.size) { + this.rotateFileSize = options.size; + this.alreadyWritten = 0; + assert.equal(typeof (this.rotateFileSize), 'number', + format('rotating-file stream "size" is not a number: %j (%s) in %j', + this.rotateFileSize, typeof (this.rotateFileSize), this)); } else { this.periodNum = 1; this.periodScope = 'd'; @@ -1149,25 +1154,52 @@ function RotatingFileStream(options) { this.rotQueue = []; this.rotating = false; this._setupNextRot(); + + var self = this; + var createStream = true; + + + if (this.rotateFileSize) { + // Check current log file size. If already too large, go ahead and rotate. + if (fs.existsSync(this.path)) { + var stats = fs.statSync(this.path); + if (stats.size > this.rotateFileSize) { + // End of the rotate call will create a new write stream, let's not do that twice. Use flag to track + // whether we need to init here or if rotate will do it for us. + createStream = false; + self.rotate(); + } else { + self.alreadyWritten = stats.size; + } + } + } + + if (createStream) { + this.stream = fs.createWriteStream(this.path, + {flags: 'a', encoding: 'utf8'}); + } + } util.inherits(RotatingFileStream, EventEmitter); RotatingFileStream.prototype._setupNextRot = function () { - var self = this; - this.rotAt = this._nextRotTime(); - var delay = this.rotAt - Date.now(); - // Cap timeout to Node's max setTimeout, see - // . - var TIMEOUT_MAX = 2147483647; // 2^31-1 - if (delay > TIMEOUT_MAX) { - delay = TIMEOUT_MAX; - } - this.timeout = setTimeout( - function () { self.rotate(); }, - delay); - if (typeof (this.timeout.unref) === 'function') { - this.timeout.unref(); + if (this.periodNum && this.periodScope) { + var self = this; + this.rotAt = this._nextRotTime(); + var delay = this.rotAt - Date.now(); + // Cap timeout to Node's max setTimeout, see + // . + var TIMEOUT_MAX = 2147483647; // 2^31-1 + if (delay > TIMEOUT_MAX) { + delay = TIMEOUT_MAX; + } + this.timeout = setTimeout( + function () { self.rotate(); }, + delay); + if (typeof (this.timeout.unref) === 'function') { + this.timeout.unref(); + } } } @@ -1258,8 +1290,10 @@ RotatingFileStream.prototype.rotate = function rotate() { // If rotation period is > ~25 days, we have to break into multiple // setTimeout's. See . - if (self.rotAt && self.rotAt > Date.now()) { - return self._setupNextRot(); + if (this.periodNum && this.periodScope) { + if (self.rotAt && self.rotAt > Date.now()) { + return self._setupNextRot(); + } } if (_DEBUG) { @@ -1271,7 +1305,11 @@ RotatingFileStream.prototype.rotate = function rotate() { } self.rotating = true; - self.stream.end(); // XXX can do moves sync after this? test at high rate + // Rotate file by size may rotate before the stream is created. Let's make sure we have a stream before trying to + // end it. + if (self.stream) { + self.stream.end(); // XXX can do moves sync after this? test at high rate + } function del() { var toDel = self.path + '.' + String(n - 1); @@ -1339,7 +1377,14 @@ RotatingFileStream.prototype.write = function write(s) { this.rotQueue.push(s); return false; } else { - return this.stream.write(s); + if ((this.alreadyWritten + this.stream.bytesWritten) > this.rotateFileSize) { + this.rotQueue.push(s); + this.rotate(); + this.alreadyWritten = 0; + return false; + } else { + return this.stream.write(s); + } } };