Permalink
Browse files

[api test] Finish work on rolling logs in the File transport

  • Loading branch information...
1 parent 7f4a007 commit 84d8ebf29cf3e7a987dcc9433664494e6ff82a2c @indexzero indexzero committed Jul 18, 2011
Showing with 177 additions and 62 deletions.
  1. +55 −37 lib/winston/internal.js
  2. +3 −3 lib/winston/transports/console.js
  3. +36 −22 lib/winston/transports/file.js
  4. +83 −0 test/file-maxsize-test.js
View
@@ -12,10 +12,13 @@ var util = require('util'),
config = require('./config');
//
-// function setLevels (target, past, current)
-// Create functions on the target objects for each level
-// in current.levels. If past is defined, remove functions
-// for each of those levels.
+// ### function setLevels (target, past, current)
+// #### @target {Object} Object on which to set levels.
+// #### @past {Object} Previous levels set on target.
+// #### @current {Object} Current levels to set on target.
+// Create functions on the target objects for each level
+// in current.levels. If past is defined, remove functions
+// for each of those levels.
//
var setLevels = exports.setLevels = function (target, past, current, isDefault) {
if (past) {
@@ -35,9 +38,9 @@ var setLevels = exports.setLevels = function (target, past, current, isDefault)
//
Object.keys(target.levels).forEach(function (level) {
target[level] = function (msg) {
- var args = Array.prototype.slice.call(arguments),
- callback = typeof args[args.length - 1] === 'function' || args.length === 3 ? args.pop() : null,
- meta = args.length === 2 ? args.pop() : null;
+ var args = Array.prototype.slice.call(arguments),
+ callback = typeof args[args.length - 1] === 'function' ? args.pop() : null,
+ meta = args.length === 2 ? args.pop() : null;
return target.log(level, msg, meta, callback);
};
@@ -46,6 +49,11 @@ var setLevels = exports.setLevels = function (target, past, current, isDefault)
return target;
};
+//
+// ### function longestElement
+// #### @xs {Array} Array to calculate against
+// Returns the longest element in the `xs` array.
+//
var longestElement = exports.longestElement = function (xs) {
return Math.max.apply(
null,
@@ -54,9 +62,10 @@ var longestElement = exports.longestElement = function (xs) {
};
//
-// function clone (obj)
-// Helper method for deep cloning pure JSON objects
-// i.e. JSON objects that are either literals or objects (no Arrays, etc)
+// ### function clone (obj)
+// #### @obj {Object} Object to clone.
+// Helper method for deep cloning pure JSON objects
+// i.e. JSON objects that are either literals or objects (no Arrays, etc)
//
var clone = exports.clone = function (obj) {
var clone = {};
@@ -67,36 +76,14 @@ var clone = exports.clone = function (obj) {
return clone;
};
-var months = ['Jan', 'Feb', 'Mar', 'Apr',
- 'May', 'Jun', 'Jul', 'Aug',
- 'Sep', 'Oct', 'Nov', 'Dec'];
-
-//
-// Borrowed from node.js core because I wanted a universal lowercase header message
-//
-var pad = exports.pad = function (n) {
- return n < 10 ? '0' + n.toString(10) : n.toString(10);
-}
-
-//
-// Borrowed from node.js core because I wanted a universal lowercase header message
-//
-var timestamp = exports.timestamp = function () {
- var d = new Date();
- var time = [pad(d.getHours()),
- pad(d.getMinutes()),
- pad(d.getSeconds())].join(':');
- return [d.getDate(), months[d.getMonth()], time].join(' ');
-}
-
//
// function log (level, msg, meta)
// Generic logging function for returning timestamped strings
//
var log = exports.log = function (level, msg, meta, options) {
- var output = options.timestamp ? timestamp() + ' - ' : '';
+ var output = options.timestamp ? timestamp() + ' - ' : '';
targetLevel = options.colorize ? config.colorize(level) : level,
- metac = exports.clone(meta);
+ metac = exports.clone(meta);
output += targetLevel + ': ' + msg;
@@ -108,10 +95,41 @@ var log = exports.log = function (level, msg, meta, options) {
}
//
-// function hash (str)
-// Utility function for creating unique ids
-// e.g. Profiling incoming HTTP requests on the same tick
+// ### function hash (str)
+// #### @str {string} String to hash.
+// Utility function for creating unique ids
+// e.g. Profiling incoming HTTP requests on the same tick
//
var hash = exports.hash = function (str) {
return crypto.createHash('sha1').update(str).digest('hex');
+};
+
+//
+// ## Borrowed from node.js core
+// I wanted a universal lowercase header message, as opposed to the `DEBUG`
+// (i.e. all uppercase header) used only in `util.debug()`
+//
+var months = ['Jan', 'Feb', 'Mar', 'Apr',
+ 'May', 'Jun', 'Jul', 'Aug',
+ 'Sep', 'Oct', 'Nov', 'Dec'];
+
+//
+// ### function pad (n)
+// Returns a padded string if `n < 10`.
+//
+var pad = exports.pad = function (n) {
+ return n < 10 ? '0' + n.toString(10) : n.toString(10);
+};
+
+//
+// ### function timestamp ()
+// Returns a timestamp string for the current time.
+//
+var timestamp = exports.timestamp = function () {
+ var d = new Date();
+ var time = [pad(d.getHours()),
+ pad(d.getMinutes()),
+ pad(d.getSeconds())].join(':');
+
+ return [d.getDate(), months[d.getMonth()], time].join(' ');
};
@@ -22,10 +22,10 @@ var Console = exports.Console = function (options) {
options = options || {};
this.name = 'console';
- this.level = options.level || 'info';
- this.silent = options.silent || false;
+ this.level = options.level || 'info';
+ this.silent = options.silent || false;
this.colorize = options.colorize || false;
- this.timestamp = options.timestamp || true;
+ this.timestamp = options.timestamp === true;
};
//
@@ -52,7 +52,7 @@ var File = exports.File = function (options) {
this.level = options.level || 'info';
this.silent = options.silent || false;
this.colorize = options.colorize || false;
- this.timestamp = options.timestamp || true;
+ this.timestamp = options.timestamp === true;
this.maxsize = options.maxsize || null;
//
@@ -126,14 +126,11 @@ File.prototype.log = function (level, msg, meta, callback) {
// (if any) and the current size of the file used.
//
File.prototype.open = function (callback) {
- var self = this;
-
- if (!this.stream || (this.maxsize && this._size >= this.maxsize)) {
- //
- // TODO: Buffer the log output before writing it
- // to
- //
- self._createStream();
+ if (this.opening) {
+ return callback(true);
+ }
+ else if (!this.stream || (this.maxsize && this._size >= this.maxsize)) {
+ this._createStream();
return callback(true);
}
@@ -153,19 +150,25 @@ File.prototype.flush = function () {
// and then write them to the newly created stream.
//
this._buffer.forEach(function (str) {
- self.stream.write(str);
- self._size += str.length;
+ process.nextTick(function () {
+ self.stream.write(str);
+ self._size += str.length;
+ });
});
//
// Quickly truncate the `_buffer` once the write operations
// have been started
//
self._buffer.length = 0;
+ self.stream.once('drain', function () {
+ self.emit('drain');
+ });
};
File.prototype._createStream = function () {
var self = this;
+ this.opening = true;
(function checkFile (target) {
var fullname = path.join(self.dirname, target);
@@ -178,6 +181,11 @@ File.prototype._createStream = function () {
self._size = size;
self.filename = target;
self.stream = fs.createWriteStream(fullname, self.options);
+
+ self.once('drain', function () {
+ self.opening = false;
+ self.emit('open', fullname);
+ });
//
// Remark: It is possible that in the time it has taken to find the
@@ -187,7 +195,7 @@ File.prototype._createStream = function () {
//
self.flush();
}
-
+
fs.stat(fullname, function (err, stats) {
if (err) {
if (err.code !== 'ENOENT') {
@@ -202,28 +210,34 @@ File.prototype._createStream = function () {
// If `stats.size` is greater than the `maxsize` for
// this instance then try again
//
- return checkFile(self._nextFile());
+ return checkFile(self._getFile(true));
}
+ console.log('stats.size', stats.size);
createAndFlush(stats.size);
});
- })(this._basename);
+ })(this._getFile());
};
//
-// ### @private function _nextFile ()
+// ### @private function _getFile ()
// Gets the next filename to use for this instance
// in the case that log filesizes are being capped.
//
-File.prototype._nextFile = function () {
+File.prototype._getFile = function (inc) {
var self = this,
ext = path.extname(this._basename),
basename = path.basename(this._basename, ext);
- //
- // Increment the number of files created or
- // checked by this instance.
- //
- this._created += 1;
- return basename + this._created + ext;
+ if (inc) {
+ //
+ // Increment the number of files created or
+ // checked by this instance.
+ //
+ this._created += 1;
+ }
+
+ return this._created
+ ? basename + this._created + ext
+ : basename + ext;
};
View
@@ -0,0 +1,83 @@
+/*
+ * file-test.js: Tests for instances of the File transport
+ *
+ * (C) 2010 Charlie Robbins
+ * MIT LICENSE
+ *
+ */
+
+require.paths.unshift(require('path').join(__dirname, '..', 'lib'));
+
+var assert = require('assert'),
+ exec = require('child_process').exec,
+ fs = require('fs'),
+ path = require('path'),
+ vows = require('vows'),
+ winston = require('winston'),
+ helpers = require('./helpers');
+
+var maxsizeTransport = new winston.transports.File({
+ timestamp: false,
+ filename: path.join(__dirname, 'testmaxsize.log'),
+ maxsize: 4096
+});
+
+vows.describe('winston/transports/file/maxsize').addBatch({
+ "An instance of the File Transport": {
+ "when passed a valid filename": {
+ "the log() method": {
+ topic: function () {
+ exec('rm -rf ' + path.join(__dirname, 'testmaxsize*'), this.callback);
+ },
+ "when passed more than the maxsize": {
+ topic: function () {
+ var that = this,
+ data = new Array(1018).join('-');
+
+ //
+ // Setup a list of files which we will later stat.
+ //
+ that.files = [];
+
+ function logKbytes (kbytes) {
+ //
+ // With no timestamp and at the info level,
+ // winston adds exactly 7 characters:
+ // [info](4)[ :](2)[\n](1)
+ //
+ for (var i = 0; i < kbytes; i++) {
+ maxsizeTransport.log('info', data, null, function () { });
+ }
+ }
+
+ maxsizeTransport.on('open', function (file) {
+ var match = file.match(/(\d+)\.log$/),
+ count = match ? match[1] : 0;
+
+ that.files.push(file);
+
+ if (parseInt(count, 10) == 5) {
+ return that.callback();
+ }
+
+ logKbytes(4);
+ });
+
+ logKbytes(4);
+ },
+ "should create multiple files correctly": function () {
+ this.files.forEach(function (file) {
+ try {
+ var stats = fs.statSync(file);
+ assert.equal(stats.size, 4096);
+ }
+ catch (ex) {
+ assert.isNull(ex);
+ }
+ });
+ }
+ }
+ }
+ }
+ }
+}).export(module);

0 comments on commit 84d8ebf

Please sign in to comment.