From fa7541564b7e76129327953857d5ceeffb940492 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Sun, 5 Nov 2017 16:18:29 -0700 Subject: [PATCH 01/21] refactoring transport to use third-party file rotation component --- .eslintrc.js | 11 + index.js | 852 +-------------------------------- legacy-daily-rotate-file.js | 261 ++++++++++ package.json | 19 +- test/legacy-transport-tests.js | 169 +++++++ test/memory-stream.js | 26 +- test/random-string.js | 29 ++ test/simple.tests.js | 793 ------------------------------ 8 files changed, 491 insertions(+), 1669 deletions(-) create mode 100644 .eslintrc.js create mode 100644 legacy-daily-rotate-file.js create mode 100644 test/legacy-transport-tests.js create mode 100644 test/random-string.js delete mode 100644 test/simple.tests.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..036841c --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,11 @@ +module.exports = exports = { + extends: 'xo', + env: { + node: true, + mocha: true + }, + rules: { + indent: ['error', 4], + camelcase: ['error', {properties: 'never'}] + } +}; diff --git a/index.js b/index.js index 42416f3..657fd61 100644 --- a/index.js +++ b/index.js @@ -1,853 +1,7 @@ 'use strict'; -var fs = require('fs'); -var path = require('path'); -var util = require('util'); -var common = require('winston/lib/winston/common'); -var Transport = require('winston').Transport; -var Stream = require('stream').Stream; -var os = require('os'); var winston = require('winston'); -var mkdirp = require('mkdirp'); -var zlib = require('zlib'); +var LegacyDailyRotateFile = require('./legacy-daily-rotate-file'); -var weekday = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; - -// -// ### function DailyRotateFile (options) -// #### @options {Object} Options for this instance. -// Constructor function for the DailyRotateFile transport object responsible -// for persisting log messages and metadata to one or more files. -// -var DailyRotateFile = module.exports = function (options) { - Transport.call(this, options); - - // - // Helper function which throws an `Error` in the event - // that any of the rest of the arguments is present in `options`. - // - function throwIf(target /* , illegal... */) { - Array.prototype.slice.call(arguments, 1).forEach(function (name) { - if (options[name]) { - throw new Error('Cannot set ' + name + ' and ' + target + 'together'); - } - }); - } - - if (options.filename || options.dirname) { - throwIf('filename or dirname', 'stream'); - this._basename = this.filename = options.filename ? - path.basename(options.filename) : - 'winston.log'; - - this.dirname = options.dirname || path.dirname(options.filename); - this.options = options.options || {flags: 'a'}; - - // - // "24 bytes" is maybe a good value for logging lines. - // - this.options.highWaterMark = this.options.highWaterMark || 24; - } else if (options.stream) { - throwIf('stream', 'filename', 'maxsize'); - this._stream = options.stream; - var self = this; - this._stream.on('error', function (error) { - self.emit('error', error); - }); - - // - // We need to listen for drain events when - // write() returns false. This can make node - // mad at times. - // - this._stream.setMaxListeners(Infinity); - } else { - throw new Error('Cannot log to file without filename or stream.'); - } - - this.json = options.json !== false; - this.colorize = options.colorize || false; - this.maxsize = options.maxsize || null; - this.logstash = options.logstash || null; - this.maxFiles = options.maxFiles || null; - this.label = options.label || null; - this.prettyPrint = options.prettyPrint || false; - this.showLevel = options.showLevel === undefined ? true : options.showLevel; - this.timestamp = options.timestamp === undefined ? true : options.timestamp; - this.datePattern = options.datePattern ? options.datePattern : '.yyyy-MM-dd'; - this.depth = options.depth || null; - this.eol = options.eol || os.EOL; - this.maxRetries = options.maxRetries || 2; - this.prepend = options.prepend || false; - this.createTree = options.createTree || false; - this.localTime = options.localTime || false; - this.zippedArchive = options.zippedArchive || false; - this.maxDays = options.maxDays || 0; - - if (this.json) { - this.stringify = options.stringify; - } - - // - // Internal state variables representing the number - // of files this instance has created and the current - // size (in bytes) of the current logfile. - // - this._size = 0; - this._created = 0; - this._buffer = []; - this._draining = false; - this._failures = 0; - this._archive = false; - - // Internal variable which will hold a record of all files - // belonging to this transport which are currently in the - // log directory in chronological order. - // - this._currentFiles = function () { - // - // Only proceed if maxsize is not configured for this transport. - if (!this.maxsize) { - try { - return fs.readdirSync(this.dirname).filter(function (file) { - return file.includes(this._basename); - }.bind(this)).map(function (file) { - return { - name: file, - time: fs.statSync(path.join(this.dirname, file)).mtime.getTime() - }; - }.bind(this)).sort(function (a, b) { - return a.time - b.time; - }).map(function (v) { - return v.name; - }); - } catch (e) { - // directory doesnt exist so there are no files. Do nothing. - } - } - return []; - }.bind(this)(); - - this._year = this._getTime('year'); - this._month = this._getTime('month'); - this._date = this._getTime('date'); - this._hour = this._getTime('hour'); - this._minute = this._getTime('minute'); - this._weekday = weekday[this._getTime('day')]; - var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhM])\1?/g; - var pad = function (val, len) { - val = String(val); - len = len || 2; - while (val.length < len) { - val = '0' + val; - } - return val; - }; - - this.getFormattedDate = function () { - // update the year, month, date... variables - this._year = this._getTime('year'); - this._month = this._getTime('month'); - this._date = this._getTime('date'); - this._hour = this._getTime('hour'); - this._minute = this._getTime('minute'); - this._weekday = weekday[this._getTime('day')]; - - var flags = { - yy: String(this._year).slice(2), - yyyy: this._year, - M: this._month + 1, - MM: pad(this._month + 1), - d: this._date, - dd: pad(this._date), - H: this._hour, - HH: pad(this._hour), - m: this._minute, - mm: pad(this._minute), - ddd: this._weekday - }; - return this.datePattern.replace(token, function ($0) { - return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1); - }); - }; -}; - -// -// Inherit from `winston.Transport`. -// -util.inherits(DailyRotateFile, Transport); - -/** - * Define a getter so that `winston.transports.DailyRotateFile` - * is available and thus backwards compatible. - */ -winston.transports.DailyRotateFile = DailyRotateFile; - -// -// Expose the name of this Transport on the prototype -// -DailyRotateFile.prototype.name = 'dailyRotateFile'; - -// -// ### function log (level, msg, [meta], callback) -// #### @level {string} Level at which to log the message. -// #### @msg {string} Message to log -// #### @meta {Object} **Optional** Additional metadata to attach -// #### @callback {function} Continuation to respond to when complete. -// Core logging method exposed to Winston. Metadata is optional. -// -DailyRotateFile.prototype.log = function (level, msg, meta, callback) { - if (this.silent) { - return callback(null, true); - } - - // - // If failures exceeds maxRetries then we can't access the - // stream. In this case we need to perform a noop and return - // an error. - // - if (this._failures >= this.maxRetries) { - return callback(new Error('Transport is in a failed state.')); - } - - var self = this; - - var output = common.log({ - level: level, - message: msg, - meta: meta, - json: this.json, - colorize: this.colorize, - logstash: this.logstash, - prettyPrint: this.prettyPrint, - timestamp: this.timestamp, - label: this.label, - stringify: this.stringify, - showLevel: this.showLevel, - depth: this.depth, - formatter: this.formatter, - humanReadableUnhandledException: this.humanReadableUnhandledException - }) + this.eol; - - this._size += output.length; - - if (this.filename) { - this.open(function (err) { - if (err) { - // - // If there was an error enqueue the message - // - return self._buffer.push([output, callback]); - } - - self._write(output, callback); - self._lazyDrain(); - }); - } else { - // - // If there is no `filename` on this instance then it was configured - // with a raw `WriteableStream` instance and we should not perform any - // size restrictions. - // - this._write(output, callback); - this._lazyDrain(); - } -}; - -// -// ### function _write (data, cb) -// #### @data {String|Buffer} Data to write to the instance's stream. -// #### @cb {function} Continuation to respond to when complete. -// Write to the stream, ensure execution of a callback on completion. -// -DailyRotateFile.prototype._write = function (data, callback) { - // If this is a file write stream, we could use the builtin - // callback functionality, however, the stream is not guaranteed - // to be an fs.WriteStream. - var ret = this._stream.write(data); - if (!callback) { - return; - } - - if (ret === false) { - return this._stream.once('drain', function () { - callback(null, true); - }); - } - callback(null, true); -}; - -// -// ### function query (options, callback) -// #### @options {Object} Loggly-like query options for this instance. -// #### @callback {function} Continuation to respond to when complete. -// Query the transport. Options object is optional. -// -DailyRotateFile.prototype.query = function (options, callback) { - if (typeof options === 'function') { - callback = options; - options = {}; - } - var self = this; - - // TODO when maxfilesize rotate occurs - var createdFiles = self._currentFiles.slice(0); // Clone already sorted _currentFiles array - var results = []; - var row = 0; - options = self.normalizeQuery(options); - - if (createdFiles.length === 0 && callback) { - callback(null, results); - } - - // Edit so that all created files are read: - (function readNextFile(nextFile) { - if (!nextFile) { - return; - } - var file = path.join(self.dirname, nextFile); - var buff = ''; - - var stream = fs.createReadStream(file, { - encoding: 'utf8' - }); - - stream.on('error', function (err) { - if (stream.readable) { - stream.destroy(); - } - if (!callback) { - return; - } - return err.code === 'ENOENT' ? callback(null, results) : callback(err); - }); - - stream.on('data', function (data) { - data = (buff + data).split(/\n+/); - var l = data.length - 1; - var i = 0; - - for (; i < l; i++) { - if (!options.start || row >= options.start) { - add(data[i]); - } - row++; - } - - buff = data[l]; - }); - - stream.on('close', function () { - if (buff) { - add(buff, true); - } - if (options.order === 'desc') { - results = results.reverse(); - } - - if (createdFiles.length) { - readNextFile(createdFiles.shift()); - } else if (callback) { - callback(null, results); - } - }); - - function add(buff, attempt) { - try { - var log = JSON.parse(buff); - if (check(log)) { - push(log); - } - } catch (e) { - if (!attempt) { - stream.emit('error', e); - } - } - } - - function push(log) { - if (options.rows && results.length >= options.rows && options.order !== 'desc') { - if (stream.readable) { - stream.destroy(); - } - return; - } - - if (options.fields) { - var obj = {}; - options.fields.forEach(function (key) { - obj[key] = log[key]; - }); - log = obj; - } - - if (options.order === 'desc') { - if (results.length >= options.rows) { - results.shift(); - } - } - results.push(log); - } - - function check(log) { - if (!log) { - return; - } - - if (typeof log !== 'object') { - return; - } - - var time = new Date(log.timestamp); - if ((options.from && time < options.from) || - (options.until && time > options.until)) { - return; - } - - return true; - } - })(createdFiles.shift());// executes the function -}; - -// -// ### function stream (options) -// #### @options {Object} Stream options for this instance. -// Returns a log stream for this transport. Options object is optional. -// -DailyRotateFile.prototype.stream = function (options) { - var file = path.join(this.dirname, this._getFilename()); - options = options || {}; - var stream = new Stream(); - - var tail = { - file: file, - start: options.start - }; - - stream.destroy = common.tailFile(tail, function (err, line) { - if (err) { - return stream.emit('error', err); - } - - try { - stream.emit('data', line); - line = JSON.parse(line); - stream.emit('log', line); - } catch (e) { - stream.emit('error', e); - } - }); - - if (stream.resume) { - stream.resume(); - } - - return stream; -}; - -// -// ### function open (callback) -// #### @callback {function} Continuation to respond to when complete -// Checks to see if a new file needs to be created based on the `maxsize` -// (if any) and the current size of the file used. -// -DailyRotateFile.prototype.open = function (callback) { - if (this.opening) { - // - // If we are already attempting to open the next - // available file then respond with a value indicating - // that the message should be buffered. - // - return callback(true); - } else if (!this._stream || (this.maxsize && this._size >= this.maxsize) || - this._filenameHasExpired()) { - this._cleanOldFiles(); - // - // If we dont have a stream or have exceeded our size, then create - // the next stream and respond with a value indicating that - // the message should be buffered. - // - callback(true); - return this._createStream(); - } - - // - // Otherwise we have a valid (and ready) stream. - // - callback(); -}; - -// -// ### function close () -// Closes the stream associated with this instance. -// -DailyRotateFile.prototype.close = function () { - var self = this; - - if (this._stream) { - this._stream.end(); - this._stream.destroySoon(); - - this._stream.once('drain', function () { - self.emit('flush'); - self.emit('closed'); - }); - } -}; - -// -// ### function flush () -// Flushes any buffered messages to the current `stream` -// used by this instance. -// -DailyRotateFile.prototype.flush = function () { - var self = this; - - // - // Iterate over the `_buffer` of enqueued messaged - // and then write them to the newly created stream. - // - this._buffer.forEach(function (item) { - var str = item[0]; - var callback = item[1]; - - process.nextTick(function () { - self._write(str, callback); - self._size += str.length; - }); - }); - - // - // Quickly truncate the `_buffer` once the write operations - // have been started - // - self._buffer.length = 0; - - // - // When the stream has drained we have flushed - // our buffer. - // - self._stream.once('drain', function () { - self.emit('flush'); - self.emit('logged'); - }); -}; - -// -// ### @private function _createStream () -// Attempts to open the next appropriate file for this instance -// based on the common state (such as `maxsize` and `_basename`). -// -DailyRotateFile.prototype._createStream = function () { - var self = this; - this.opening = true; - - (function checkFile(target) { - var fullname = path.join(self.dirname, target); - // - // Creates the `WriteStream` and then flushes any - // buffered messages. - // - function createAndFlush(size) { - if (self._stream) { - self._archive = self.zippedArchive ? self._stream.path : false; - - self._stream.end(); - self._stream.destroySoon(); - } - - if (self.createTree) { - mkdirp.sync(path.dirname(fullname)); - } - - self._size = size; - self.filename = target; - self._stream = fs.createWriteStream(fullname, self.options); - self._stream.on('error', function (error) { - if (self._failures < self.maxRetries) { - self._createStream(); - self._failures++; - } else { - self.emit('error', error); - } - }); - - // - // We need to listen for drain events when - // write() returns false. This can make node - // mad at times. - // - self._stream.setMaxListeners(Infinity); - - // - // When the current stream has finished flushing - // then we can be sure we have finished opening - // and thus can emit the `open` event. - // - self.once('flush', function () { - // Because "flush" event is based on native stream "drain" event, - // logs could be written inbetween "self.flush()" and here - // Therefore, we need to flush again to make sure everything is flushed - self.flush(); - - self.opening = false; - self.emit('open', fullname); - }); - - // - // Remark: It is possible that in the time it has taken to find the - // next logfile to be written more data than `maxsize` has been buffered, - // but for sensible limits (10s - 100s of MB) this seems unlikely in less - // than one second. - // - self.flush(); - compressFile(); - } - - function compressFile() { - var logfile = self._archive; - self._archive = false; - if (logfile && fs.existsSync(String(logfile))) { - var gzip = zlib.createGzip(); - - var inp = fs.createReadStream(String(logfile)); - var out = fs.createWriteStream(logfile + '.gz'); - - inp.pipe(gzip).pipe(out); - fs.unlinkSync(String(logfile)); - } - } - - fs.stat(fullname, function (err, stats) { - if (err) { - if (err.code !== 'ENOENT') { - return self.emit('error', err); - } - - return createAndFlush(0); - } - - if (!stats || (self.maxsize && stats.size >= self.maxsize)) { - // - // If `stats.size` is greater than the `maxsize` for - // this instance then try again - // - return checkFile(self._getFile(true)); - } - - if (self._filenameHasExpired()) { - self._year = self._getTime('year'); - self._month = self._getTime('month'); - self._date = self._getTime('date'); - self._hour = self._getTime('hour'); - self._minute = self._getTime('minute'); - self._weekday = weekday[self._getTime('day')]; - self._created = 0; - return checkFile(self._getFile()); - } - - createAndFlush(stats.size); - }); - })(this._getFile()); -}; - -// -// ### @private function _getFile () -// Gets the next filename to use for this instance -// in the case that log filesizes are being capped. -// -DailyRotateFile.prototype._getFile = function (inc) { - var filename = this._getFilename(); - var remaining; - - if (inc) { - // - // Increment the number of files created or - // checked by this instance. - // - // Check for maxFiles option and delete file - if (this.maxFiles && (this._created >= (this.maxFiles - 1))) { - remaining = this._created - (this.maxFiles - 1); - if (remaining === 0) { - try { - fs.unlinkSync(path.join(this.dirname, filename)); - } catch (e) {} - } else { - try { - fs.unlinkSync(path.join(this.dirname, filename + '.' + remaining)); - } catch (e) {} - } - } - - this._created += 1; - } else if (!this.maxsize) { - // - // If the filename does not exist in the _currentFiles array then add it. - if (this._currentFiles.indexOf(filename) === -1) { - this._currentFiles.push(filename); - } - - // While the _currentFiles array contains more file names than is configured - // in maxFiles loop the _currentFiles array and delete the file found at el - // 0. - while (this.maxFiles && (this._currentFiles.length > this.maxFiles)) { - try { - fs.unlinkSync(path.join(this.dirname, this._currentFiles[0])); - } catch (e) { - // File isn't accessible, do nothing. - } - - // Remove the filename that was just deleted from the _currentFiles array. - this._currentFiles = this._currentFiles.slice(1); - } - } - - return this._created ? filename + '.' + this._created : filename; -}; - -// -// ### @private function _getFilename () -// Returns the log filename depending on `this.prepend` option value -// -DailyRotateFile.prototype._getFilename = function () { - var formattedDate = this.getFormattedDate(); - - if (this.prepend) { - if (this.datePattern === '.yyyy-MM-dd') { - this.datePattern = 'yyyy-MM-dd.'; - formattedDate = this.getFormattedDate(); - } - - return formattedDate + this._basename; - } - - return this._basename + formattedDate; -}; - -// -// ### @private function _lazyDrain () -// Lazily attempts to emit the `logged` event when `this.stream` has -// drained. This is really just a simple mutex that only works because -// Node.js is single-threaded. -// -DailyRotateFile.prototype._lazyDrain = function () { - var self = this; - - if (!this._draining && this._stream) { - this._draining = true; - - this._stream.once('drain', function () { - this._draining = false; - self.emit('logged'); - }); - } -}; - -// -// ### @private function _filenameHasExpired () -// Checks whether the current log file is valid -// based on given datepattern -// -DailyRotateFile.prototype._filenameHasExpired = function () { - // searching for m is enough to say minute in date pattern - if (this.datePattern.match(/m/)) { - return (this._year < this._getTime('year') || this._month < this._getTime('month') || this._date < this._getTime('date') || this._hour < this._getTime('hour') || this._minute < this._getTime('minute')); - } else if (this.datePattern.match(/H/)) { - return (this._year < this._getTime('year') || this._month < this._getTime('month') || this._date < this._getTime('date') || this._hour < this._getTime('hour')); - } else if (this.datePattern.match(/d/)) { - return (this._year < this._getTime('year') || this._month < this._getTime('month') || this._date < this._getTime('date')); - } else if (this.datePattern.match(/M/)) { - return (this._year < this._getTime('year') || this._month < this._getTime('month')); - } else if (this.datePattern.match(/yy/)) { - return (this._year < this._getTime('year')); - } - return false; -}; - -// -// ### @private function _getTime () -// Get current date/time -// based on localTime config -// -DailyRotateFile.prototype._getTime = function (timeType) { - var now = new Date(); - - if (this.localTime) { - if (timeType === 'year') { - return now.getFullYear(); - } else if (timeType === 'month') { - return now.getMonth(); - } else if (timeType === 'date') { - return now.getDate(); - } else if (timeType === 'hour') { - return now.getHours(); - } else if (timeType === 'minute') { - return now.getMinutes(); - } else if (timeType === 'day') { - return now.getDay(); - } - } - if (timeType === 'year') { - return now.getUTCFullYear(); - } else if (timeType === 'month') { - return now.getUTCMonth(); - } else if (timeType === 'date') { - return now.getUTCDate(); - } else if (timeType === 'hour') { - return now.getUTCHours(); - } else if (timeType === 'minute') { - return now.getUTCMinutes(); - } else if (timeType === 'day') { - return now.getUTCDay(); - } -}; - -// ### @private function _cleanOldFiles () -// Remove old log files -// based on "maxDays" option -DailyRotateFile.prototype._cleanOldFiles = function () { - var self = this; - var millisecondsInDay = 86400000; - var now = Date.now(); - - function removeOldFile(file) { - fs.unlink(self.dirname + path.sep + file, function (errUnlink) { - if (errUnlink) { - console.error('Error removing file ', file); - } - }); - } - - function tryToRemoveLogFile(file) { - var completeFileName = self.dirname + path.sep + file; - fs.stat(completeFileName, function (errStats, stats) { - if (errStats) { - console.error('Error stats file ', file, errStats); - return; - } - - var lastChangeTimestamp = ((stats.mtime && stats.mtime.getTime()) || 0); - var lifeTime = now - lastChangeTimestamp; - if (stats.isFile() && lifeTime > (millisecondsInDay * self.maxDays)) { - removeOldFile(file); - } - }); - } - - // if not maxDays specified, do not remove old log files - if (self.maxDays) { - fs.readdir(self.dirname, function (err, files) { - if (err) { - console.error('Error reading directory ', self.dirname, err); - return; - } - - var fileNameReg = new RegExp(self._basename, 'g'); - files.forEach(function (file) { - if (/.log/.test(file) && fileNameReg.test(file)) { - tryToRemoveLogFile(file); - } - }); - }); - } -}; +winston.transports.DailyRotateFile = LegacyDailyRotateFile; +module.exports = LegacyDailyRotateFile; diff --git a/legacy-daily-rotate-file.js b/legacy-daily-rotate-file.js new file mode 100644 index 0000000..24ccffa --- /dev/null +++ b/legacy-daily-rotate-file.js @@ -0,0 +1,261 @@ +'use strict'; + +var fs = require('fs'); +var os = require('os'); +var path = require('path'); +var util = require('util'); +var winston = require('winston'); +var common = require('winston/lib/winston/common'); +var Transport = winston.Transport; +var zlib = require('zlib'); + +var loggerDefaults = { + json: false, + colorize: false, + eol: os.EOL, + logstash: null, + prettyPrint: false, + label: null, + stringify: false, + depth: null, + showLevel: true, + timestamp: true +}; + +var LegacyDailyRotateFile = function (options) { + if (!options) { + options = {}; + } + + Transport.call(this, options); + + function throwIf(target /* , illegal... */) { + Array.prototype.slice.call(arguments, 1).forEach(function (name) { + if (options[name]) { + throw new Error('Cannot set ' + name + ' and ' + target + ' together'); + } + }); + } + + function getMaxSize(size) { + if (size && typeof size === 'string') { + var _s = size.toLowerCase().match(/^((?:0\.)?\d+)([k|m|g])$/); + if (_s) { + return size; + } + } else if (size && Number.isInteger(size)) { + var sizeK = Math.round(size / 1024); + return sizeK === 0 ? '1k' : sizeK + 'k'; + } + return null; + } + + this.options = Object.assign({}, loggerDefaults, options); + + if (options.filename || options.dirname) { + throwIf('filename or dirname', 'stream'); + this.filename = options.filename ? path.basename(options.filename) : 'winston.log'; + this.dirname = options.dirname || path.dirname(options.filename); + + var self = this; + + this.logStream = require('file-stream-rotator').getStream({ + filename: path.join(this.dirname, this.filename), + frequency: 'custom', + date_format: options.datePattern ? options.datePattern : 'YYYY-MM-DD', + verbose: false, + size: getMaxSize(options.maxSize), + max_logs: options.maxFiles + }); + + this.logStream.on('close', function () { + self.emit('close'); + }); + + this.logStream.on('finish', function () { + self.emit('finish'); + }); + + this.logStream.on('error', function (err) { + self.emit('error', err); + }); + + this.logStream.on('open', function (fd) { + self.emit('open', fd); + }); + + if (options.zippedArchive) { + this.logStream.on('rotate', function (oldFile) { + var gzip = zlib.createGzip(); + var inp = fs.createReadStream(oldFile); + var out = fs.createWriteStream(oldFile + '.gz'); + inp.pipe(gzip).pipe(out).on('finish', function () { + fs.unlinkSync(oldFile); + }); + }); + } + } else if (options.stream) { + throwIf('stream', 'filename', 'maxsize'); + this.logStream = options.stream; + } else { + throw new Error('Cannot log to file without filename or stream.'); + } +}; + +module.exports = LegacyDailyRotateFile; + +util.inherits(LegacyDailyRotateFile, Transport); + +LegacyDailyRotateFile.prototype.name = 'dailyRotateFile'; + +LegacyDailyRotateFile.prototype.log = function (level, msg, meta, callback) { + var self = this; + + var options = Object.assign({}, self.options, { + level: level, + message: msg, + meta: meta + }); + + var output = common.log(options) + options.eol; + self.logStream.write(output); + + if (callback) { + callback(null, true); + } +}; + +LegacyDailyRotateFile.prototype.close = function () { + if (this.logStream) { + this.logStream.end(); + } +}; + +LegacyDailyRotateFile.prototype.query = function (options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } + + if (!this.filename) { + throw new Error('query() may not be used when initializing with a stream'); + } + + var self = this; + var results = []; + var row = 0; + options = self.normalizeQuery(options); + + var logFiles = (function () { + var fileRegex = new RegExp(self.filename.replace('%DATE%', '.*'), 'i'); + return fs.readdirSync(self.dirname).filter(function (file) { + return path.basename(file).match(fileRegex); + }); + })(); + + if (logFiles.length === 0 && callback) { + callback(null, results); + } + + (function processLogFile(file) { + if (!file) { + return; + } + var logFile = path.join(self.dirname, file); + var buff = ''; + + var stream = fs.createReadStream(logFile, { + encoding: 'utf8' + }); + + stream.on('error', function (err) { + if (stream.readable) { + stream.destroy(); + } + if (!callback) { + return; + } + return err.code === 'ENOENT' ? callback(null, results) : callback(err); + }); + + stream.on('data', function (data) { + data = (buff + data).split(/\n+/); + var l = data.length - 1; + + for (var i = 0; i < l; i++) { + if (!options.start || row >= options.start) { + add(data[i]); + } + row++; + } + + buff = data[l]; + }); + + stream.on('close', function () { + if (buff) { + add(buff, true); + } + + if (options.order === 'desc') { + results = results.reverse(); + } + + if (logFiles.length) { + processLogFile(logFiles.shift()); + } else if (callback) { + callback(null, results); + } + }); + + function add(buff, attempt) { + try { + var log = JSON.parse(buff); + if (check(log)) { + push(log); + } + } catch (e) { + if (!attempt) { + stream.emit('error', e); + } + } + } + + function check(log) { + if (!log || typeof log !== 'object') { + return; + } + + var time = new Date(log.timestamp); + if ((options.from && time < options.from) || (options.until && time > options.until)) { + return; + } + + return true; + } + + function push(log) { + if (options.rows && results.length >= options.rows && options.order !== 'desc') { + if (stream.readable) { + stream.destroy(); + } + return; + } + + if (options.fields) { + var obj = {}; + options.fields.forEach(function (key) { + obj[key] = log[key]; + }); + log = obj; + } + + if (options.order === 'desc') { + if (results.length >= options.rows) { + results.shift(); + } + } + results.push(log); + } + })(logFiles.shift()); +}; diff --git a/package.json b/package.json index a8bed67..82f531a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "winston-daily-rotate-file", - "version": "1.7.2", + "version": "2.0.0-beta", "description": "A transport for winston which logs to a rotating file each day.", "main": "index.js", "scripts": { @@ -29,21 +29,12 @@ }, "devDependencies": { "chai": "3.5.0", - "eslint": "2.10.2", - "eslint-config-xo-space": "0.13.0", + "eslint": "^4.10.0", + "eslint-config-xo": "^0.19.0", "mocha": "2.4.5", - "moment": "2.13.0", - "rimraf": "2.5.2", - "timekeeper": "^0.1.1" - }, - "eslintConfig": { - "extends": "xo-space", - "env": { - "node": true, - "mocha": true - } + "rimraf": "2.5.2" }, "dependencies": { - "mkdirp": "0.5.1" + "file-stream-rotator": "^0.2.0" } } diff --git a/test/legacy-transport-tests.js b/test/legacy-transport-tests.js new file mode 100644 index 0000000..5fdb08d --- /dev/null +++ b/test/legacy-transport-tests.js @@ -0,0 +1,169 @@ +/* eslint-disable max-nested-callbacks,no-unused-expressions,handle-callback-err */ +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var expect = require('chai').expect; +var rimraf = require('rimraf'); +var moment = require('moment'); +var MemoryStream = require('./memory-stream'); +var randomString = require('./random-string'); +var LegacyDailyRotateFile = require('../legacy-daily-rotate-file'); + +describe('winston/transports/daily-rotate-file', function () { + beforeEach(function () { + this.stream = new MemoryStream(); + this.transport = new LegacyDailyRotateFile({ + json: true, + stream: this.stream + }); + }); + + it('should have the proper methods defined', function () { + var transport = new LegacyDailyRotateFile({stream: new MemoryStream()}); + expect(transport).to.be.instanceOf(LegacyDailyRotateFile); + expect(transport).to.respondTo('log'); + expect(transport).to.respondTo('query'); + }); + + it('should write to the stream', function (done) { + var self = this; + this.transport.log('info', 'this message should write to the stream', {}, function (err, logged) { + expect(err).to.be.null; + expect(logged).to.be.true; + console.lo; + var logEntry = JSON.parse(self.stream.toString()); + expect(logEntry.level).to.equal('info'); + expect(logEntry.message).to.equal('this message should write to the stream'); + done(); + }); + }); + + describe('when passed metadata', function () { + var circular = {}; + circular.metadata = circular; + + var params = { + no: {}, + object: {metadata: true}, + primitive: 'metadata', + circular: circular + }; + + Object.keys(params).forEach(function (param) { + it('should accept log messages with ' + param + ' metadata', function (done) { + this.transport.log('info', 'test log message', params[param], function (err, logged) { + expect(err).to.be.null; + expect(logged).to.be.true; + // TODO parse the metadata value to make sure its set properly + done(); + }); + }); + }); + }); + + describe('when using a filename or dirname', function () { + var logDir = path.join(__dirname, 'logs'); + var now = moment().format('YYYY-MM-DD-HH'); + var filename = path.join(logDir, 'application-' + now + '.log'); + var options = { + json: true, + dirname: logDir, + filename: 'application-%DATE%.log', + datePattern: 'YYYY-MM-DD-HH' + }; + + beforeEach(function (done) { + var self = this; + rimraf(logDir, function () { + self.transport = new LegacyDailyRotateFile(options); + done(); + }); + }); + + it('should write to the file', function (done) { + this.transport.on('finish', function () { + var logEntries = fs.readFileSync(filename).toString().split('\n').slice(0, -1); + expect(logEntries.length).to.equal(1); + + var logEntry = JSON.parse(logEntries[0]); + expect(logEntry.level).to.equal('info'); + expect(logEntry.message).to.equal('this message should write to the file'); + done(); + }); + + this.transport.log('info', 'this message should write to the file', {}, function (err, logged) { + expect(err).to.be.null; + expect(logged).to.be.true; + }); + + this.transport.close(); + }); + + it('should not allow the stream to be set', function () { + var opts = Object.assign({}, options); + opts.stream = new MemoryStream(); + expect(function () { + var transport = new LegacyDailyRotateFile(opts); + expect(transport).to.not.be.null; + }).to.throw(); + }); + + describe('when setting zippedArchive', function () { + it('should archive the log after rotating', function (done) { + var self = this; + var opts = Object.assign({}, options); + opts.zippedArchive = true; + opts.maxSize = '1k'; + + this.transport = new LegacyDailyRotateFile(opts); + + this.transport.on('finish', function () { + fs.readdir(logDir, function (err, files) { + expect(files.filter(function (file) { + return path.extname(file) === '.gz'; + }).length).to.equal(1); + done(); + }); + }); + this.transport.log('info', randomString(1056)); + this.transport.log('info', randomString(1056)); + self.transport.close(); + }); + }); + + describe('query', function () { + it('should call callback when no files are present', function () { + this.transport.query(function (err, results) { + expect(results).to.not.be.null; + expect(results.length).to.equal(0); + }); + }); + + it('should raise error when calling with stream', function () { + expect(function () { + var transport = new LegacyDailyRotateFile({stream: new MemoryStream()}); + transport.query(null); + }).to.throw(); + }); + + it('should return log entries that match the query', function (done) { + this.transport.log('info', randomString(1056)); + this.transport.log('info', randomString(1056)); + this.transport.log('info', randomString(1056)); + this.transport.log('info', randomString(1056)); + + this.transport.close(); + + var self = this; + this.transport.on('finish', function () { + self.transport.query(function (err, results) { + expect(results).to.not.be.null; + expect(results.length).to.equal(4); + done(); + }); + }); + }); + }); + }); +}); diff --git a/test/memory-stream.js b/test/memory-stream.js index 6e944cb..ccafbe1 100644 --- a/test/memory-stream.js +++ b/test/memory-stream.js @@ -8,31 +8,31 @@ module.exports = WritableStream; util.inherits(WritableStream, Stream.Writable); function WritableStream(options) { - Stream.Writable.call(this, options); + Stream.Writable.call(this, options); } WritableStream.prototype.write = function () { - var ret = Stream.Writable.prototype.write.apply(this, arguments); - if (!ret) { - this.emit('drain'); - } + var ret = Stream.Writable.prototype.write.apply(this, arguments); + if (!ret) { + this.emit('drain'); + } - return ret; + return ret; }; WritableStream.prototype._write = function (chunk, encoding, callback) { - this.write(chunk, encoding, callback); + this.write(chunk, encoding, callback); }; WritableStream.prototype.toString = function () { - return this.toBuffer().toString(); + return this.toBuffer().toString(); }; WritableStream.prototype.toBuffer = function () { - var buffers = []; - this._writableState.buffer.forEach(function (data) { - buffers.push(data.chunk); - }); + var buffers = []; + this._writableState.getBuffer().forEach(function (data) { + buffers.push(data.chunk); + }); - return Buffer.concat(buffers); + return Buffer.concat(buffers); }; diff --git a/test/random-string.js b/test/random-string.js new file mode 100644 index 0000000..31ae7f3 --- /dev/null +++ b/test/random-string.js @@ -0,0 +1,29 @@ +var uppers = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; +var lowers = 'abcdefghijklmnopqrstuvwxyz'; +var numbers = '0123456789'; +var specials = '_-|@.,?/!~#$%^&*(){}[]+='; +var charClasses = [uppers, lowers, numbers, specials]; +var minLen = charClasses.length; +function chooseRandom(x) { + var i = Math.floor(Math.random() * x.length); + return (typeof (x) === 'string') ? x.substr(i, 1) : x[i]; +} + +module.exports = function (maxLen) { + maxLen = (maxLen || 36); + if (maxLen < minLen) { + throw new Error('length must be >= ' + minLen); + } + var str = ''; + var usedClasses = {}; + var charClass; + do { // Append a random char from a random char class. + while (str.length < maxLen) { + charClass = chooseRandom(charClasses); + usedClasses[charClass] = true; + str += chooseRandom(charClass); + } + // Ensure we have picked from every char class. + } while (Object.keys(usedClasses).length !== charClasses.length); + return str; +}; diff --git a/test/simple.tests.js b/test/simple.tests.js deleted file mode 100644 index 8bfb7f8..0000000 --- a/test/simple.tests.js +++ /dev/null @@ -1,793 +0,0 @@ -/* eslint-disable max-nested-callbacks,no-unused-expressions */ -'use strict'; - -var path = require('path'); -var expect = require('chai').expect; -var winston = require('winston'); -var rimraf = require('rimraf'); -var mkdirp = require('mkdirp'); -var moment = require('moment'); -var fs = require('fs'); -var tk = require('timekeeper'); -var MemoryStream = require('./memory-stream'); - -var DailyRotateFile = require('../'); - -var fixturesDir = path.join(__dirname, 'fixtures'); - -var transports = { - 'file': new DailyRotateFile({ - filename: path.join(fixturesDir, 'testfilename.log'), - prepend: false - }), - 'stream': new DailyRotateFile({stream: new MemoryStream()}), - 'prepended file': new DailyRotateFile({ - filename: path.join(fixturesDir, 'testfilename.log'), - prepend: true - }), - 'weekday file': new DailyRotateFile({ - filename: path.join(fixturesDir, 'testfilename_weekday'), - datePattern: '.ddd.log' - }), - 'prepend weekday file': new DailyRotateFile({ - filename: path.join(fixturesDir, 'testfilename_prepend_weekday.log'), - datePattern: 'ddd-', - prepend: true - }) -}; - -describe('winston/transports/daily-rotate-file', function () { - before(function () { - rimraf.sync(fixturesDir); - mkdirp.sync(fixturesDir); - }); - - describe('an instance of the transport', function () { - describe('with / characters in the datePattern', function () { - it('should create the full path', function (done) { - var now = moment(); - var transport = new DailyRotateFile({ - filename: path.join(fixturesDir, 'application'), - datePattern: '/yyyy/MM/dd.log', - createTree: true - }); - - transport.log('info', 'test message', {}, function (err) { - if (err) { - done(err); - } - - fs.readFile(path.join(fixturesDir, 'application', now.format('YYYY'), now.format('MM'), now.format('DD') + '.log'), 'utf8', function (err, contents) { - if (err) { - done(err); - } - - var lines = contents.split('\n').filter(function (n) { - return n !== ''; - }); - - expect(lines.length).to.equal(1); - done(); - }); - }); - }); - }); - - describe('with default datePatterns', function () { - it('should have a proper filename when prepend option is false', function () { - var now = moment().utc().format('YYYY-MM-DD'); - var transport = new DailyRotateFile({ - filename: path.join(fixturesDir, 'prepend-false.log'), - prepend: false - }); - - expect(transport._getFilename()).to.equal('prepend-false.log.' + now); - }); - - it('should have a proper filename when prepend option is false (localtime)', function () { - var now = moment().format('YYYY-MM-DD'); - var transport = new DailyRotateFile({ - filename: path.join(fixturesDir, 'prepend-false.log'), - localTime: true, - prepend: false - }); - - expect(transport._getFilename()).to.equal('prepend-false.log.' + now); - }); - - it('should have a proper filename when prepend options is true', function () { - var now = moment().utc().format('YYYY-MM-DD'); - var transport = new DailyRotateFile({ - filename: path.join(fixturesDir, 'prepend-true.log'), - prepend: true - }); - - expect(transport._getFilename()).to.equal(now + '.prepend-true.log'); - }); - - it('should remove leading dot if one is provided with datePattern', function () { - var now = moment().utc().format('YYYYMMDD'); - var transport = new DailyRotateFile({ - filename: path.join(fixturesDir, 'prepend-false.log'), - prepend: false, - datePattern: '.yyyyMMdd' - }); - - expect(transport._getFilename()).to.equal('prepend-false.log.' + now); - }); - - it('should not add leading dot if one is not provided with datePattern', function () { - var now = moment().utc().format('YYYY-MM-DD'); - var transport = new DailyRotateFile({ - filename: path.join(fixturesDir, 'log'), - datePattern: '-yyyy-MM-dd.log' - }); - - expect(transport._getFilename()).to.equal('log-' + now + '.log'); - }); - - it('should remove leading dot if one is provided with datePattern when prepend option is true', function () { - var now = moment().utc().format('YYYY-MM-DD'); - var transport = new DailyRotateFile({ - filename: path.join(fixturesDir, 'prepend-true.log'), - prepend: true, - datePattern: '.yyyy-MM-dd' - }); - - expect(transport._getFilename()).to.equal(now + '.prepend-true.log'); - }); - }); - - Object.keys(transports).forEach(function (t) { - describe('when passed a valid ' + t, function () { - var transport; - - beforeEach(function () { - transport = transports[t]; - }); - - it('should have the proper methods defined', function () { - expect(transport).to.be.instanceOf(DailyRotateFile); - expect(transport).to.respondTo('log'); - }); - - var levels = winston.config.npm.levels; - Object.keys(levels).forEach(function (level) { - describe('with the ' + level + ' level', function () { - it('should respond with true when passed no metadata', function (done) { - transport.log(level, 'test message', {}, function (err, logged) { - expect(err).to.be.null; - expect(logged).to.be.true; - done(); - }); - }); - - var circular = { }; - circular.metadata = circular; - - var params = { - no: {}, - object: {metadata: true}, - primitive: 'metadata', - circular: circular - }; - - Object.keys(params).forEach(function (param) { - it('should respond with true when passed ' + param + ' metadata', function (done) { - transport.log(level, 'test message', params[param], function (err, logged) { - expect(err).to.be.null; - expect(logged).to.be.true; - done(); - }); - }); - }); - }); - }); - }); - }); - - describe('when passed an invalid filename', function () { - var transport; - - beforeEach(function () { - transport = new DailyRotateFile({ - filename: path.join(fixturesDir, 'invalid', 'testfilename.log') - }); - }); - - it('should have proper methods defined', function () { - expect(transport).to.be.instanceOf(DailyRotateFile); - expect(transport).to.respondTo('log'); - }); - - it('should enter noop failed state', function (done) { - transport.on('error', function (emitErr) { - expect(emitErr).to.be.instanceOf(Error); - expect(emitErr.code, 'ENOENT'); - expect(transport._failures).to.equal(transport.maxRetries); - done(); - }); - - transport.log('error', 'test message'); - }); - }); - - describe('when passed an valid filename with different date patterns for log rotation', function () { - // patterns having one start timestamp for which log file will be creted, - // then one mid timestamp for which log file should not be rotated, - // and finally one end timestamp for which log file should be rotated and - // new logfile should be created. - var patterns = { - 'full year pattern .yyyy': { - pattern: '.yyyy', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1874993560000, // GMT: Fri, 01 Jun 2029 07:32:40 GMT - end: 1893483160000, // GMT: Tue, 01 Jan 2030 07:32:40 GMT - oldfile: 'test-rotation.log.2029', - newfile: 'test-rotation.log.2030' - }, - 'small year pattern .yy': { - pattern: '.yy', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1874993560000, // GMT: Fri, 01 Jun 2029 07:32:40 GMT - end: 1893483160000, // GMT: Tue, 01 Jan 2030 07:32:40 GMT - oldfile: 'test-rotation.log.29', - newfile: 'test-rotation.log.30' - }, - 'month pattern .M': { - pattern: '.M', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1863156760000, // GMT: Mon, 15 Jan 2029 07:32:40 GMT - end: 1864625560000, // GMT: Thu, 01 Feb 2029 07:32:40 GMT - oldfile: 'test-rotation.log.1', - newfile: 'test-rotation.log.2' - }, - 'zero padded month pattern .MM': { - pattern: '.MM', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1863156760000, // GMT: Mon, 15 Jan 2029 07:32:40 GMT - end: 1864625560000, // GMT: Thu, 01 Feb 2029 07:32:40 GMT - oldfile: 'test-rotation.log.01', - newfile: 'test-rotation.log.02' - }, - 'daypattern .d': { - pattern: '.d', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1861986760000, // GMT: Mon, 01 Jan 2029 18:32:40 GMT - end: 1863156760000, // GMT: Mon, 15 Jan 2029 07:32:40 GMT - oldfile: 'test-rotation.log.1', - newfile: 'test-rotation.log.15' - }, - 'zero padded day pattern .dd': { - pattern: '.dd', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1861986760000, // GMT: Mon, 01 Jan 2029 18:32:40 GMT - end: 1863156760000, // GMT: Mon, 15 Jan 2029 07:32:40 GMT - oldfile: 'test-rotation.log.01', - newfile: 'test-rotation.log.15' - }, - 'hour pattern .H': { - pattern: '.H', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1861947760000, // GMT: Mon, 01 Jan 2029 07:42:40 GMT - end: 1861950760000, // GMT: Mon, 01 Jan 2029 08:32:40 GMT - oldfile: 'test-rotation.log.7', - newfile: 'test-rotation.log.8' - }, - 'zero padded hour pattern .HH': { - pattern: '.HH', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1861947760000, // GMT: Mon, 01 Jan 2029 07:42:40 GMT - end: 1861950760000, // GMT: Mon, 01 Jan 2029 08:32:40 GMT - oldfile: 'test-rotation.log.07', - newfile: 'test-rotation.log.08' - }, - 'minute pattern .m': { - pattern: '.m', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:50 GMT - mid: 1861947170000, // GMT: Mon, 01 Jan 2029 07:32:50 GMT - end: 1861947760000, // GMT: Mon, 01 Jan 2029 07:42:40 GMT - oldfile: 'test-rotation.log.32', - newfile: 'test-rotation.log.42' - }, - 'zero padded minute pattern .mm': { - pattern: '.mm', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:50 GMT - mid: 1861947170000, // GMT: Mon, 01 Jan 2029 07:32:50 GMT - end: 1861947760000, // GMT: Mon, 01 Jan 2029 07:42:40 GMT - oldfile: 'test-rotation.log.32', - newfile: 'test-rotation.log.42' - }, - 'daily rotation pattern .yyyy-MM-dd': { - pattern: '.yyyy-MM-dd', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1861965828000, // GMT: Mon, 01 Jan 2029 12:43:48 GMT - end: 1863156760000, // GMT: Mon, 15 Jan 2029 07:32:40 GMT - oldfile: 'test-rotation.log.2029-01-01', - newfile: 'test-rotation.log.2029-01-15' - } - }; - Object.keys(patterns).forEach(function (pattern) { - describe('when passed the pattern ' + pattern, function () { - var transport; - var rotationLogPath = path.join(fixturesDir, 'rotations'); - - beforeEach(function (done) { - this.time = new Date(patterns[pattern].start); - tk.travel(this.time); - rimraf.sync(rotationLogPath); - mkdirp.sync(rotationLogPath); - transport = new DailyRotateFile({ - filename: path.join(rotationLogPath, 'test-rotation.log'), - datePattern: patterns[pattern].pattern - }); - - done(); - }); - - afterEach(function (done) { - tk.reset(); - done(); - }); - - it('should create log with proper timestamp', function (done) { - var self = this; - - transport.log('error', 'test message', {}, function (err) { - if (err) { - done(err); - } - - var filesCreated = fs.readdirSync(rotationLogPath); - expect(filesCreated.length).to.eql(1); - expect(filesCreated).to.include(patterns[pattern].oldfile); - self.time = new Date(patterns[pattern].mid); - tk.travel(self.time); - transport.log('error', '2nd test message', {}, function (err) { - if (err) { - done(err); - } - - filesCreated = fs.readdirSync(rotationLogPath); - expect(filesCreated.length).to.eql(1); - expect(filesCreated).to.include(patterns[pattern].oldfile); - self.time = new Date(patterns[pattern].end); - tk.travel(self.time); - transport.log('error', '3rd test message', {}, function (err) { - if (err) { - done(err); - } - - filesCreated = fs.readdirSync(rotationLogPath); - expect(filesCreated.length).to.eql(2); - expect(filesCreated).to.include(patterns[pattern].newfile); - transport.close(); - - done(); - }); - }); - }); - }); - }); - }); - }); - - describe('when passed with maxsize and maxfiles', function () { - var dailyRotationPattern = { - pattern: '.yyyy-MM-dd', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1861986760000, // GMT: Mon, 01 Jan 2029 18:32:40 GMT - file1: 'test-rotation.log.2029-01-01', - file2: 'test-rotation.log.2029-01-01.1', - file3: 'test-rotation.log.2029-01-01.2' - }; - - describe('when passed the pattern ' + dailyRotationPattern.pattern, function () { - var transport; - var rotationLogPath = path.join(fixturesDir, 'rotations'); - - beforeEach(function (done) { - this.time = new Date(dailyRotationPattern.start); - tk.travel(this.time); - rimraf.sync(rotationLogPath); - mkdirp.sync(rotationLogPath); - transport = new DailyRotateFile({ - filename: path.join(rotationLogPath, 'test-rotation.log'), - datePattern: dailyRotationPattern.pattern, - maxFiles: 2, - maxsize: 100 - }); - - done(); - }); - - afterEach(function (done) { - tk.reset(); - done(); - }); - - it('should properly rotate log with old files getting deleted', function (done) { - var self = this; - - transport.log('error', 'test message with more than 100 bytes data', {}, function (err) { - if (err) { - done(err); - } - - transport.log('error', '2nd test with more than 100 bytes data', {}, function (err) { - if (err) { - done(err); - } - - self.time = new Date(dailyRotationPattern.mid); - tk.travel(self.time); - transport.log('error', '3rd test', {}, function (err) { - if (err) { - done(err); - } - - transport.log('error', '4th test message with more than 100 bytes data', {}, function (err) { - if (err) { - done(err); - } - - var filesCreated = fs.readdirSync(rotationLogPath); - expect(filesCreated.length).to.eql(2); - expect(filesCreated).not.to.include(dailyRotationPattern.file1); - expect(filesCreated).to.include(dailyRotationPattern.file2); - expect(filesCreated).to.include(dailyRotationPattern.file3); - done(); - }); - }); - }); - }); - }); - }); - }); - - describe('when passed with maxfiles set and maxsize not set', function () { - var dailyRotationPattern = { - pattern: '.yyyy-MM-dd', - jan1: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - jan2: 1862033560000, // GMT: Mon, 02 Jan 2029 07:32:40 GMT - jan3: 1862119960000, // GMT: Mon, 03 Jan 2029 07:32:40 GMT - file1: 'test-rotation-no-maxsize.log.2029-01-01', - file2: 'test-rotation-no-maxsize.log.2029-01-02', - file3: 'test-rotation-no-maxsize.log.2029-01-03' - }; - - describe('when passed the pattern ' + dailyRotationPattern.pattern + ' and no maxsize', function () { - var transport; - var rotationLogPath = path.join(fixturesDir, 'rotations_no_maxsize'); - - beforeEach(function (done) { - rimraf.sync(rotationLogPath); - mkdirp.sync(rotationLogPath); - transport = new DailyRotateFile({ - filename: path.join(rotationLogPath, 'test-rotation-no-maxsize.log'), - datePattern: dailyRotationPattern.pattern, - maxFiles: 2 - }); - - done(); - }); - - afterEach(function () { - tk.reset(); - }); - - it('should properly rotate log without maxzsize set and with old files getting deleted', function (done) { - var self = this; - self.time = new Date(dailyRotationPattern.jan1); - tk.travel(self.time); - - transport.log('error', 'test message on Jan 1st', {}, function (err) { - if (err) { - done(err); - } - - self.time = new Date(dailyRotationPattern.jan2); - tk.travel(self.time); - - transport.log('error', 'test message on Jan 2nd', {}, function (err) { - if (err) { - done(err); - } - - self.time = new Date(dailyRotationPattern.jan3); - tk.travel(self.time); - - transport.log('error', 'test message on Jan 3rd', {}, function (err) { - if (err) { - done(err); - } - - self.time = new Date(dailyRotationPattern.jan3); - tk.travel(self.time); - - transport.log('error', 'second test message on Jan 3rd', {}, function (err) { - if (err) { - done(err); - } - - var filesCreated = fs.readdirSync(rotationLogPath); - console.log('files : ' + filesCreated); - expect(filesCreated.length).to.eql(2); - expect(filesCreated).not.to.include(dailyRotationPattern.file1); - expect(filesCreated).to.include(dailyRotationPattern.file2); - expect(filesCreated).to.include(dailyRotationPattern.file3); - done(); - }); - }); - }); - }); - }); - }); - }); - - describe('when zippedArchive is true and passed an valid filename with different date patterns for log rotation', function () { - // patterns having one start timestamp for which log file will be creted, - // then one mid timestamp for which log file should not be rotated, - // and finally one end timestamp for which log file should be rotated and - // new logfile should be created. - var patterns = { - 'full year pattern .yyyy': { - pattern: '.yyyy', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1874993560000, // GMT: Fri, 01 Jun 2029 07:32:40 GMT - end: 1893483160000, // GMT: Tue, 01 Jan 2030 07:32:40 GMT - oldfile: 'test-rotation.log.2029', - newfile: 'test-rotation.log.2030' - }, - 'small year pattern .yy': { - pattern: '.yy', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1874993560000, // GMT: Fri, 01 Jun 2029 07:32:40 GMT - end: 1893483160000, // GMT: Tue, 01 Jan 2030 07:32:40 GMT - oldfile: 'test-rotation.log.29', - newfile: 'test-rotation.log.30' - }, - 'month pattern .M': { - pattern: '.M', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1863156760000, // GMT: Mon, 15 Jan 2029 07:32:40 GMT - end: 1864625560000, // GMT: Thu, 01 Feb 2029 07:32:40 GMT - oldfile: 'test-rotation.log.1', - newfile: 'test-rotation.log.2' - }, - 'zero padded month pattern .MM': { - pattern: '.MM', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1863156760000, // GMT: Mon, 15 Jan 2029 07:32:40 GMT - end: 1864625560000, // GMT: Thu, 01 Feb 2029 07:32:40 GMT - oldfile: 'test-rotation.log.01', - newfile: 'test-rotation.log.02' - }, - 'daypattern .d': { - pattern: '.d', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1861986760000, // GMT: Mon, 01 Jan 2029 18:32:40 GMT - end: 1863156760000, // GMT: Mon, 15 Jan 2029 07:32:40 GMT - oldfile: 'test-rotation.log.1', - newfile: 'test-rotation.log.15' - }, - 'zero padded day pattern .dd': { - pattern: '.dd', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1861986760000, // GMT: Mon, 01 Jan 2029 18:32:40 GMT - end: 1863156760000, // GMT: Mon, 15 Jan 2029 07:32:40 GMT - oldfile: 'test-rotation.log.01', - newfile: 'test-rotation.log.15' - }, - 'hour pattern .H': { - pattern: '.H', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1861947760000, // GMT: Mon, 01 Jan 2029 07:42:40 GMT - end: 1861950760000, // GMT: Mon, 01 Jan 2029 08:32:40 GMT - oldfile: 'test-rotation.log.7', - newfile: 'test-rotation.log.8' - }, - 'zero padded hour pattern .HH': { - pattern: '.HH', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1861947760000, // GMT: Mon, 01 Jan 2029 07:42:40 GMT - end: 1861950760000, // GMT: Mon, 01 Jan 2029 08:32:40 GMT - oldfile: 'test-rotation.log.07', - newfile: 'test-rotation.log.08' - }, - 'minute pattern .m': { - pattern: '.m', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:50 GMT - mid: 1861947170000, // GMT: Mon, 01 Jan 2029 07:32:50 GMT - end: 1861947760000, // GMT: Mon, 01 Jan 2029 07:42:40 GMT - oldfile: 'test-rotation.log.32', - newfile: 'test-rotation.log.42' - }, - 'zero padded minute pattern .mm': { - pattern: '.mm', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:50 GMT - mid: 1861947170000, // GMT: Mon, 01 Jan 2029 07:32:50 GMT - end: 1861947760000, // GMT: Mon, 01 Jan 2029 07:42:40 GMT - oldfile: 'test-rotation.log.32', - newfile: 'test-rotation.log.42' - }, - 'daily rotation pattern .yyyy-MM-dd': { - pattern: '.yyyy-MM-dd', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:40 GMT - mid: 1861965828000, // GMT: Mon, 01 Jan 2029 12:43:48 GMT - end: 1863156760000, // GMT: Mon, 15 Jan 2029 07:32:40 GMT - oldfile: 'test-rotation.log.2029-01-01', - newfile: 'test-rotation.log.2029-01-15' - } - }; - Object.keys(patterns).forEach(function (pattern) { - describe('when passed the pattern ' + pattern, function () { - var transport; - var rotationLogPath = path.join(fixturesDir, 'rotations'); - - beforeEach(function (done) { - this.time = new Date(patterns[pattern].start); - tk.travel(this.time); - rimraf.sync(rotationLogPath); - mkdirp.sync(rotationLogPath); - transport = new DailyRotateFile({ - filename: path.join(rotationLogPath, 'test-rotation.log'), - datePattern: patterns[pattern].pattern, - zippedArchive: true - }); - - done(); - }); - - afterEach(function () { - tk.reset(); - }); - - it('should create log with proper timestamp', function (done) { - var self = this; - - transport.log('error', 'test message', {}, function (err) { - if (err) { - done(err); - } - - var filesCreated = fs.readdirSync(rotationLogPath); - expect(filesCreated.length).to.eql(1); - expect(filesCreated).to.include(patterns[pattern].oldfile); - self.time = new Date(patterns[pattern].mid); - tk.travel(self.time); - transport.log('error', '2nd test message', {}, function (err) { - if (err) { - done(err); - } - - filesCreated = fs.readdirSync(rotationLogPath); - expect(filesCreated.length).to.eql(1); - expect(filesCreated).to.include(patterns[pattern].oldfile); - self.time = new Date(patterns[pattern].end); - tk.travel(self.time); - transport.log('error', '3rd test message', {}, function (err) { - if (err) { - done(err); - } - - filesCreated = fs.readdirSync(rotationLogPath); - expect(filesCreated.length).to.eql(2); - expect(filesCreated).to.include(patterns[pattern].newfile); - expect(filesCreated).to.include(patterns[pattern].oldfile + '.gz'); - transport.close(); - - done(); - }); - }); - }); - }); - }); - }); - }); - }); - - describe('when zippedArchive is true', function () { - var pattern = { - pattern: '.m', - start: 1861947160000, // GMT: Mon, 01 Jan 2029 07:32:50 GMT - mid: 1861947240000, // GMT: Mon, 01 Jan 2029 07:34:00 GMT - end: 1861947760000, // GMT: Mon, 01 Jan 2029 07:42:40 GMT - oldfile: 'test-rotation.log.32', - midfile: 'test-rotation.log.34', - newfile: 'test-rotation.log.42' - }; - - var transport; - var rotationLogPath = path.join(fixturesDir, 'rotations'); - beforeEach(function (done) { - this.time = new Date(pattern.start); - tk.travel(this.time); - rimraf.sync(rotationLogPath); - mkdirp.sync(rotationLogPath); - transport = new DailyRotateFile({ - filename: path.join(rotationLogPath, 'test-rotation.log'), - datePattern: pattern.pattern, - zippedArchive: true - }); - - done(); - }); - - afterEach(function () { - tk.reset(); - }); - - it('should compress logfile even if there is only one log message', function (done) { - var self = this; - - transport.log('error', 'test message', {}, function (err) { - if (err) { - done(err); - } - - var filesCreated = fs.readdirSync(rotationLogPath); - expect(filesCreated.length).to.eql(1); - expect(filesCreated).to.include(pattern.oldfile); - self.time = new Date(pattern.end); - tk.travel(self.time); - transport.log('error', '2nd test message', {}, function (err) { - if (err) { - done(err); - } - - filesCreated = fs.readdirSync(rotationLogPath); - expect(filesCreated.length).to.eql(2); - expect(filesCreated).to.include(pattern.newfile); - expect(filesCreated).to.include(pattern.oldfile + '.gz'); - transport.close(); - - done(); - }); - }); - }); - - it('should compress logfile without adding count to file extension if there are no files with the same name', function (done) { - var self = this; - - transport.log('error', 'test message', {}, function (err) { - if (err) { - done(err); - } - - var filesCreated = fs.readdirSync(rotationLogPath); - expect(filesCreated.length).to.eql(1); - expect(filesCreated).to.include(pattern.oldfile); - self.time = new Date(pattern.mid); - tk.travel(self.time); - transport.log('error', '2nd test message', {}, function (err) { - if (err) { - done(err); - } - - filesCreated = fs.readdirSync(rotationLogPath); - expect(filesCreated.length).to.eql(2); - expect(filesCreated).to.include(pattern.oldfile + '.gz'); - expect(filesCreated).to.include(pattern.midfile); - self.time = new Date(pattern.end); - tk.travel(self.time); - transport.log('error', '3rd test message', {}, function (err) { - if (err) { - done(err); - } - - filesCreated = fs.readdirSync(rotationLogPath); - expect(filesCreated.length).to.eql(3); - expect(filesCreated).to.not.include(pattern.newfile + '.1'); - expect(filesCreated).to.include(pattern.newfile); - expect(filesCreated).to.include(pattern.midfile + '.gz'); - expect(filesCreated).to.include(pattern.oldfile + '.gz'); - transport.close(); - - done(); - }); - }); - }); - }); - }); -}); From 9ba063367d4fd09e2360a462281cd83c67f7287d Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Sun, 5 Nov 2017 21:46:36 -0700 Subject: [PATCH 02/21] setting some more sane defaults --- legacy-daily-rotate-file.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/legacy-daily-rotate-file.js b/legacy-daily-rotate-file.js index 24ccffa..b243b2a 100644 --- a/legacy-daily-rotate-file.js +++ b/legacy-daily-rotate-file.js @@ -52,8 +52,10 @@ var LegacyDailyRotateFile = function (options) { this.options = Object.assign({}, loggerDefaults, options); - if (options.filename || options.dirname) { - throwIf('filename or dirname', 'stream'); + if (options.stream) { + throwIf('stream', 'filename', 'maxsize'); + this.logStream = options.stream; + } else { this.filename = options.filename ? path.basename(options.filename) : 'winston.log'; this.dirname = options.dirname || path.dirname(options.filename); @@ -94,11 +96,6 @@ var LegacyDailyRotateFile = function (options) { }); }); } - } else if (options.stream) { - throwIf('stream', 'filename', 'maxsize'); - this.logStream = options.stream; - } else { - throw new Error('Cannot log to file without filename or stream.'); } }; From 07c31d00e6ea588d4734bd1c2b06024e8e035eb7 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Sun, 5 Nov 2017 21:46:48 -0700 Subject: [PATCH 03/21] emitting rotate event --- legacy-daily-rotate-file.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/legacy-daily-rotate-file.js b/legacy-daily-rotate-file.js index b243b2a..bad4e0c 100644 --- a/legacy-daily-rotate-file.js +++ b/legacy-daily-rotate-file.js @@ -86,6 +86,10 @@ var LegacyDailyRotateFile = function (options) { self.emit('open', fd); }); + this.logStream.on('rotate', function (oldFile, newFile) { + self.emit('rotate', oldFile, newFile); + }); + if (options.zippedArchive) { this.logStream.on('rotate', function (oldFile) { var gzip = zlib.createGzip(); From 407f0cba2504b8a112f8860f977d5774342792fa Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Sun, 5 Nov 2017 21:47:01 -0700 Subject: [PATCH 04/21] Documentation update --- README.md | 59 +++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 9cdb80b..bb9a33f 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,41 @@ [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url] -> A transport for winston which logs to a rotating file each day. +A transport for [winston](https://github.com/winstonjs/winston) which logs to a rotating file. Logs can be rotated based on a date, size limit, and old logs can be removed based on count or elapsed days. -## Usage +Starting with version 2.0.0, the transport has been refactored to leverage the the [file-stream-rotator](https://github.com/rogerc/file-stream-rotator/) module. _Some of the options in the 1.x versions of the transport have changed._ Please review the options below to identify any changes needed. + +## Install +``` +npm install winston-daily-rotate-file +``` +## Options +The DailyRotateFile transport can rotate files by minute, hour, day, month, year or weekday. In addition to the options accepted by the logger, `winston-daily-rotate-file` also accepts the following options: + +* **datePattern:** A string representing the [moment.js date format](http://momentjs.com/docs/#/displaying/format/) to be used for rotating. The meta characters used in this string will dictate the frequency of the file rotation. For example, if your datePattern is simply 'HH' you will end up with 24 log files that are picked up and appended to every day. (default 'YYYY-MM-DD') +* **zippedArchive:** A boolean to define whether or not to gzip archived log files. (default 'false') +* **filename:** Filename to be used to log to. This filename can include the `%DATE%` placeholder which will include the formatted datePattern at that point in the filename. (default: 'winston.log.%DATE%) +* **dirname:** The directory name to save log files to. (default: '.') +* **stream:** Write directly to a custom stream and bypass the rotation capabilities. (default: null) +* **maxSize:** Maximum size of the file after which it will rotate. This can be a number of bytes, or units of kb, mb, and gb. If using the units, add 'k', 'm', or 'g' as the suffix. The units need to directly follow the number. (default: null) +* **maxFiles:** Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null) + +## Usage ``` js var winston = require('winston'); require('winston-daily-rotate-file'); var transport = new (winston.transports.DailyRotateFile)({ - filename: './log', - datePattern: 'yyyy-MM-dd.', - prepend: true, - level: process.env.ENV === 'development' ? 'debug' : 'info' + filename: 'application-%DATE%.log', + datePattern: 'YYYY-MM-DD-HH', + zippedArchive: true, + maxSize: '20m', + maxFiles: '14d' + }); + + transport.on('rotate', function(oldFilename, newFilename) { + // do something fun }); var logger = new (winston.Logger)({ @@ -26,30 +48,7 @@ logger.info('Hello World!'); ``` -The DailyRotateFile transport can rotate files by minute, hour, day, month, year or weekday. In addition to the options accepted by the File transport, the Daily Rotate File Transport also accepts the following options: - -* __datePattern:__ A string representing the pattern to be used when appending the date to the filename (default 'yyyy-MM-dd'). The meta characters used in this string will dictate the frequency of the file rotation. For example, if your datePattern is simply 'HH' you will end up with 24 log files that are picked up and appended to every day. -* __prepend:__ Defines if the rolling time of the log file should be prepended at the beginning of the filename (default 'false'). -* __localTime:__ A boolean to define whether time stamps should be local (default 'false' means that UTC time will be used). -* __zippedArchive:__ A boolean to define whether or not to gzip archived log files (default 'false'). -* __maxDays:__ A number representing the maximum number of days a log file will be saved. Any log file older than this specified number of days will be removed. If not value or a 0, no log files will be removed. -* __createTree:__ When combined with a `datePattern` that includes path delimiters, the transport will create the entire folder tree to the log file. Example: `datePattern: '/yyyy/MM/dd.log', createTree: true` will create the entire path to the log file prior to writing an entry. - -Valid meta characters in the datePattern are: - -* __yy:__ Last two digits of the year. -* __yyyy:__ Full year. -* __M:__ The month. -* __MM:__ The zero padded month. -* __d:__ The day. -* __dd:__ The zero padded day. -* __H:__ The hour. -* __HH:__ The zero padded hour. -* __m:__ The minute. -* __mm:__ The zero padded minute. -* __ddd:__ The weekday (Mon, Tue, ..., Sun). - -*Metadata:* Logged via util.inspect(meta); +You can listen to the *open*, *close*, *error*, and *finish* events generated by the underlying stream. You can also listen for the *rotate* custom event. The rotate event will pass two parameters to the callback (*oldFilename*, *newFilename*). ## LICENSE MIT From e569e020e88e752a9ec17912554b4366bb1c7e29 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Sun, 5 Nov 2017 22:08:36 -0700 Subject: [PATCH 05/21] adding missing dependency --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 82f531a..49201cb 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "eslint": "^4.10.0", "eslint-config-xo": "^0.19.0", "mocha": "2.4.5", + "moment": "^2.19.1", "rimraf": "2.5.2" }, "dependencies": { From b028bc3726353f9586f439b2f7bb60bfbf3642b3 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Sun, 5 Nov 2017 22:47:27 -0700 Subject: [PATCH 06/21] removing support for nodejs versions < 4. Adding node 6 and 8 to travis build. --- .travis.yml | 10 +++++++--- package.json | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6bdf26d..d86e514 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,10 @@ sudo: false language: node_js node_js: - - "0.10" - - "0.12" - - "4.1" + - "4" + - "6" + - "8" + +before_install: + - "npm install winston@^2" + diff --git a/package.json b/package.json index 49201cb..9cecf54 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,9 @@ "version": "2.0.0-beta", "description": "A transport for winston which logs to a rotating file each day.", "main": "index.js", + "engines": { + "node": ">=4" + }, "scripts": { "test": "mocha && eslint .", "preversion": "npm test", From 9e1c68d61d9b7496b9cade3414480005105a97c8 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Wed, 31 Jan 2018 14:59:41 -0700 Subject: [PATCH 07/21] adding winston 3.0.0-rc1 compatibility --- .travis.yml | 6 +- ...ily-rotate-file.js => daily-rotate-file.js | 55 ++++++++++++------- index.js | 6 +- package.json | 6 +- ...-transport-tests.js => transport-tests.js} | 48 ++++++++++------ 5 files changed, 76 insertions(+), 45 deletions(-) rename legacy-daily-rotate-file.js => daily-rotate-file.js (82%) rename test/{legacy-transport-tests.js => transport-tests.js} (75%) diff --git a/.travis.yml b/.travis.yml index d86e514..877f4a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,10 @@ node_js: - "6" - "8" +env: + - WINSTON_VER=winston@^2 + - WINSTON_VER=winston@next + before_install: - - "npm install winston@^2" + - npm install $WINSTON_VER diff --git a/legacy-daily-rotate-file.js b/daily-rotate-file.js similarity index 82% rename from legacy-daily-rotate-file.js rename to daily-rotate-file.js index bad4e0c..a465b58 100644 --- a/legacy-daily-rotate-file.js +++ b/daily-rotate-file.js @@ -4,10 +4,11 @@ var fs = require('fs'); var os = require('os'); var path = require('path'); var util = require('util'); -var winston = require('winston'); -var common = require('winston/lib/winston/common'); -var Transport = winston.Transport; +var semver = require('semver'); var zlib = require('zlib'); +var winston = require('winston'); +var compat = require('winston-compat'); +var Transport = semver.major(winston.version) === 2 ? compat.Transport : require('winston-transport'); var loggerDefaults = { json: false, @@ -19,10 +20,12 @@ var loggerDefaults = { stringify: false, depth: null, showLevel: true, - timestamp: true + timestamp: function () { + return new Date().toISOString(); + } }; -var LegacyDailyRotateFile = function (options) { +var DailyRotateFile = function (options) { if (!options) { options = {}; } @@ -103,36 +106,46 @@ var LegacyDailyRotateFile = function (options) { } }; -module.exports = LegacyDailyRotateFile; +module.exports = DailyRotateFile; -util.inherits(LegacyDailyRotateFile, Transport); +util.inherits(DailyRotateFile, Transport); -LegacyDailyRotateFile.prototype.name = 'dailyRotateFile'; +DailyRotateFile.prototype.name = 'dailyRotateFile'; -LegacyDailyRotateFile.prototype.log = function (level, msg, meta, callback) { - var self = this; - - var options = Object.assign({}, self.options, { - level: level, - message: msg, - meta: meta - }); - - var output = common.log(options) + options.eol; - self.logStream.write(output); +if (semver.major(winston.version) === 2) { + DailyRotateFile.prototype.log = function (level, msg, meta, callback) { + var options = Object.assign({}, this.options, { + level: level, + message: msg, + meta: meta + }); + this._internalLog(options, callback); + }; +} else { + DailyRotateFile.prototype.normalizeQuery = compat.Transport.prototype.normalizeQuery; + DailyRotateFile.prototype.log = function (info, callback) { + var options = Object.assign({}, this.options, info); + this._internalLog(options, callback); + }; +} + +DailyRotateFile.prototype._internalLog = function (options, callback) { + var opts = Object.assign({}, this.options, options); + var output = compat.log(opts) + this.options.eol; + this.logStream.write(output); if (callback) { callback(null, true); } }; -LegacyDailyRotateFile.prototype.close = function () { +DailyRotateFile.prototype.close = function () { if (this.logStream) { this.logStream.end(); } }; -LegacyDailyRotateFile.prototype.query = function (options, callback) { +DailyRotateFile.prototype.query = function (options, callback) { if (typeof options === 'function') { callback = options; options = {}; diff --git a/index.js b/index.js index 657fd61..c818660 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,7 @@ 'use strict'; var winston = require('winston'); -var LegacyDailyRotateFile = require('./legacy-daily-rotate-file'); +var DailyRotateFile = require('./daily-rotate-file'); -winston.transports.DailyRotateFile = LegacyDailyRotateFile; -module.exports = LegacyDailyRotateFile; +winston.transports.DailyRotateFile = DailyRotateFile; +module.exports = DailyRotateFile; diff --git a/package.json b/package.json index 9cecf54..8bb122b 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ }, "homepage": "https://github.com/winstonjs/winston-daily-rotate-file#readme", "peerDependencies": { - "winston": "2.x" + "winston": "^2 <= 3.0.0-rc1 || ^3" }, "devDependencies": { "chai": "3.5.0", @@ -39,6 +39,8 @@ "rimraf": "2.5.2" }, "dependencies": { - "file-stream-rotator": "^0.2.0" + "file-stream-rotator": "^0.2.0", + "semver": "^5.5.0", + "winston-compat": "0.0.1" } } diff --git a/test/legacy-transport-tests.js b/test/transport-tests.js similarity index 75% rename from test/legacy-transport-tests.js rename to test/transport-tests.js index 5fdb08d..d8ca9a5 100644 --- a/test/legacy-transport-tests.js +++ b/test/transport-tests.js @@ -6,32 +6,44 @@ var path = require('path'); var expect = require('chai').expect; var rimraf = require('rimraf'); var moment = require('moment'); +var semver = require('semver'); +var winston = require('winston'); var MemoryStream = require('./memory-stream'); var randomString = require('./random-string'); -var LegacyDailyRotateFile = require('../legacy-daily-rotate-file'); +var DailyRotateFile = require('../daily-rotate-file'); + +function sendLogItem(transport, level, message, meta, cb) { // eslint-disable-line max-params + if (semver.major(winston.version) === 2) { + transport.log(level, message, meta); + } else { + transport.log({ + level: level, + message: message + }, cb); + } +} describe('winston/transports/daily-rotate-file', function () { beforeEach(function () { this.stream = new MemoryStream(); - this.transport = new LegacyDailyRotateFile({ + this.transport = new DailyRotateFile({ json: true, stream: this.stream }); }); it('should have the proper methods defined', function () { - var transport = new LegacyDailyRotateFile({stream: new MemoryStream()}); - expect(transport).to.be.instanceOf(LegacyDailyRotateFile); + var transport = new DailyRotateFile({stream: new MemoryStream()}); + expect(transport).to.be.instanceOf(DailyRotateFile); expect(transport).to.respondTo('log'); expect(transport).to.respondTo('query'); }); it('should write to the stream', function (done) { var self = this; - this.transport.log('info', 'this message should write to the stream', {}, function (err, logged) { + sendLogItem(this.transport, 'info', 'this message should write to the stream', {}, function (err, logged) { expect(err).to.be.null; expect(logged).to.be.true; - console.lo; var logEntry = JSON.parse(self.stream.toString()); expect(logEntry.level).to.equal('info'); expect(logEntry.message).to.equal('this message should write to the stream'); @@ -52,7 +64,7 @@ describe('winston/transports/daily-rotate-file', function () { Object.keys(params).forEach(function (param) { it('should accept log messages with ' + param + ' metadata', function (done) { - this.transport.log('info', 'test log message', params[param], function (err, logged) { + sendLogItem(this.transport, 'info', 'test log message', params[param], function (err, logged) { expect(err).to.be.null; expect(logged).to.be.true; // TODO parse the metadata value to make sure its set properly @@ -76,7 +88,7 @@ describe('winston/transports/daily-rotate-file', function () { beforeEach(function (done) { var self = this; rimraf(logDir, function () { - self.transport = new LegacyDailyRotateFile(options); + self.transport = new DailyRotateFile(options); done(); }); }); @@ -92,7 +104,7 @@ describe('winston/transports/daily-rotate-file', function () { done(); }); - this.transport.log('info', 'this message should write to the file', {}, function (err, logged) { + sendLogItem(this.transport, 'info', 'this message should write to the file', {}, function (err, logged) { expect(err).to.be.null; expect(logged).to.be.true; }); @@ -104,7 +116,7 @@ describe('winston/transports/daily-rotate-file', function () { var opts = Object.assign({}, options); opts.stream = new MemoryStream(); expect(function () { - var transport = new LegacyDailyRotateFile(opts); + var transport = new DailyRotateFile(opts); expect(transport).to.not.be.null; }).to.throw(); }); @@ -116,7 +128,7 @@ describe('winston/transports/daily-rotate-file', function () { opts.zippedArchive = true; opts.maxSize = '1k'; - this.transport = new LegacyDailyRotateFile(opts); + this.transport = new DailyRotateFile(opts); this.transport.on('finish', function () { fs.readdir(logDir, function (err, files) { @@ -126,8 +138,8 @@ describe('winston/transports/daily-rotate-file', function () { done(); }); }); - this.transport.log('info', randomString(1056)); - this.transport.log('info', randomString(1056)); + sendLogItem(this.transport, 'info', randomString(1056)); + sendLogItem(this.transport, 'info', randomString(1056)); self.transport.close(); }); }); @@ -142,16 +154,16 @@ describe('winston/transports/daily-rotate-file', function () { it('should raise error when calling with stream', function () { expect(function () { - var transport = new LegacyDailyRotateFile({stream: new MemoryStream()}); + var transport = new DailyRotateFile({stream: new MemoryStream()}); transport.query(null); }).to.throw(); }); it('should return log entries that match the query', function (done) { - this.transport.log('info', randomString(1056)); - this.transport.log('info', randomString(1056)); - this.transport.log('info', randomString(1056)); - this.transport.log('info', randomString(1056)); + sendLogItem(this.transport, 'info', randomString(1056)); + sendLogItem(this.transport, 'info', randomString(1056)); + sendLogItem(this.transport, 'info', randomString(1056)); + sendLogItem(this.transport, 'info', randomString(1056)); this.transport.close(); From 9af7adc1b162c6621530697385e71f30442cab52 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Wed, 31 Jan 2018 15:06:19 -0700 Subject: [PATCH 08/21] adding callback to test --- test/transport-tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/transport-tests.js b/test/transport-tests.js index d8ca9a5..0ce341c 100644 --- a/test/transport-tests.js +++ b/test/transport-tests.js @@ -14,7 +14,7 @@ var DailyRotateFile = require('../daily-rotate-file'); function sendLogItem(transport, level, message, meta, cb) { // eslint-disable-line max-params if (semver.major(winston.version) === 2) { - transport.log(level, message, meta); + transport.log(level, message, meta, cb); } else { transport.log({ level: level, From 2e50195c8adc58f8e13435ae4cbadb803f1e41be Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Wed, 31 Jan 2018 15:12:18 -0700 Subject: [PATCH 09/21] version bump for rc --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8bb122b..78fdc77 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "winston-daily-rotate-file", - "version": "2.0.0-beta", + "version": "2.0.0-rc.1", "description": "A transport for winston which logs to a rotating file each day.", "main": "index.js", "engines": { From a3e62a979e373265d572afdd06d373b493474a93 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Wed, 31 Jan 2018 15:28:48 -0700 Subject: [PATCH 10/21] ignoring test files --- .gitignore | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 07cf972..6067a47 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # Logs -logs -*.log -*.log.* +*log* +test.js # Runtime data pids From f3ba4b398ce480d2ee9d79f98606cf52fbd3202e Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Thu, 1 Feb 2018 11:54:39 -0700 Subject: [PATCH 11/21] updating for winston3 changes in formatting --- daily-rotate-file.js | 32 +- package-lock.json | 1335 +++++++++++++++++++++++++++++++++++++++ test/transport-tests.js | 14 +- 3 files changed, 1362 insertions(+), 19 deletions(-) create mode 100644 package-lock.json diff --git a/daily-rotate-file.js b/daily-rotate-file.js index a465b58..5f10d7e 100644 --- a/daily-rotate-file.js +++ b/daily-rotate-file.js @@ -8,6 +8,7 @@ var semver = require('semver'); var zlib = require('zlib'); var winston = require('winston'); var compat = require('winston-compat'); +var PassThrough = require('stream').PassThrough; var Transport = semver.major(winston.version) === 2 ? compat.Transport : require('winston-transport'); var loggerDefaults = { @@ -26,10 +27,7 @@ var loggerDefaults = { }; var DailyRotateFile = function (options) { - if (!options) { - options = {}; - } - + options = options || {}; Transport.call(this, options); function throwIf(target /* , illegal... */) { @@ -57,7 +55,8 @@ var DailyRotateFile = function (options) { if (options.stream) { throwIf('stream', 'filename', 'maxsize'); - this.logStream = options.stream; + this.logStream = new PassThrough(); + this.logStream.pipe(options.stream); } else { this.filename = options.filename ? path.basename(options.filename) : 'winston.log'; this.dirname = options.dirname || path.dirname(options.filename); @@ -112,32 +111,31 @@ util.inherits(DailyRotateFile, Transport); DailyRotateFile.prototype.name = 'dailyRotateFile'; +var noop = function () {}; if (semver.major(winston.version) === 2) { DailyRotateFile.prototype.log = function (level, msg, meta, callback) { + callback = callback || noop; var options = Object.assign({}, this.options, { level: level, message: msg, meta: meta }); - this._internalLog(options, callback); + var output = compat.log(options) + options.eol; + this.logStream.write(output); + callback(null, true); }; } else { DailyRotateFile.prototype.normalizeQuery = compat.Transport.prototype.normalizeQuery; DailyRotateFile.prototype.log = function (info, callback) { - var options = Object.assign({}, this.options, info); - this._internalLog(options, callback); - }; -} + var MESSAGE = Symbol.for('message'); + callback = callback || noop; -DailyRotateFile.prototype._internalLog = function (options, callback) { - var opts = Object.assign({}, this.options, options); - var output = compat.log(opts) + this.options.eol; - this.logStream.write(output); - if (callback) { + this.logStream.write(info[MESSAGE] + this.options.eol); + this.emit('logged', info); callback(null, true); - } -}; + }; +} DailyRotateFile.prototype.close = function () { if (this.logStream) { diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..44ac40c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1335 @@ +{ + "name": "winston-daily-rotate-file", + "version": "2.0.0-rc.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "acorn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.3.0.tgz", + "integrity": "sha512-Yej+zOJ1Dm/IMZzzj78OntP/r3zHEaKcyNoU2lAaxPtrseM6rF0xwqoz5Q5ysAiED9hTjI2hgtvLXitlCN1/Ug==", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "3.3.0" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true + }, + "ansi-escapes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz", + "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, + "chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", + "dev": true, + "requires": { + "assertion-error": "1.1.0", + "deep-eql": "0.1.3", + "type-detect": "1.0.0" + } + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", + "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + } + }, + "cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "dev": true, + "requires": { + "type-detect": "0.1.1" + }, + "dependencies": { + "type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", + "dev": true + } + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.0", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.5.2" + } + }, + "diff": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", + "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", + "dev": true + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "2.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.16.0.tgz", + "integrity": "sha512-YVXV4bDhNoHHcv0qzU4Meof7/P26B4EuaktMi5L1Tnt52Aov85KmYA8c5D+xyZr/BkhvwUqr011jDSD/QTULxg==", + "dev": true, + "requires": { + "ajv": "5.5.2", + "babel-code-frame": "6.26.0", + "chalk": "2.3.0", + "concat-stream": "1.6.0", + "cross-spawn": "5.1.0", + "debug": "3.1.0", + "doctrine": "2.1.0", + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "1.0.0", + "espree": "3.5.2", + "esquery": "1.0.0", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "functional-red-black-tree": "1.0.1", + "glob": "7.1.2", + "globals": "11.3.0", + "ignore": "3.3.7", + "imurmurhash": "0.1.4", + "inquirer": "3.3.0", + "is-resolvable": "1.1.0", + "js-yaml": "3.10.0", + "json-stable-stringify-without-jsonify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.4", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "7.0.0", + "progress": "2.0.0", + "require-uncached": "1.0.3", + "semver": "5.5.0", + "strip-ansi": "4.0.0", + "strip-json-comments": "2.0.1", + "table": "4.0.2", + "text-table": "0.2.0" + } + }, + "eslint-config-xo": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/eslint-config-xo/-/eslint-config-xo-0.19.0.tgz", + "integrity": "sha512-RMGQv8UGWS/r8/2RT143H5BmESncz6i2LMEs/unUw1OjJwbeR8qaUOt4eAOHTggdWQlQlR8Y6XhPfgaPXDLKMA==", + "dev": true + }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "4.2.0", + "estraverse": "4.2.0" + } + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "dev": true + }, + "espree": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.2.tgz", + "integrity": "sha512-sadKeYwaR/aJ3stC2CdvgXu1T16TdYN+qwCpcWbMnGJ8s0zNWemzrvb2GbD4OhmJ/fwpJjudThAlLobGbWZbCQ==", + "dev": true, + "requires": { + "acorn": "5.3.0", + "acorn-jsx": "3.0.1" + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "esquery": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", + "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", + "dev": true, + "requires": { + "estraverse": "4.2.0" + } + }, + "esrecurse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", + "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", + "dev": true, + "requires": { + "estraverse": "4.2.0", + "object-assign": "4.1.1" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "external-editor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.1.0.tgz", + "integrity": "sha512-E44iT5QVOUJBKij4IIV3uvxuNlbKS38Tw1HiupxEIHPv9qtC2PrDYohbXV5U+1jnfIXttny8gUhj+oZvflFlzA==", + "dev": true, + "requires": { + "chardet": "0.4.2", + "iconv-lite": "0.4.19", + "tmp": "0.0.33" + } + }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "1.3.0", + "object-assign": "4.1.1" + } + }, + "file-stream-rotator": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.2.0.tgz", + "integrity": "sha1-nXY4MGlQ7YO9dYYmh4SxqUh+CKs=", + "requires": { + "moment": "2.20.1" + } + }, + "flat-cache": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", + "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", + "dev": true, + "requires": { + "circular-json": "0.3.3", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "globals": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.3.0.tgz", + "integrity": "sha512-kkpcKNlmQan9Z5ZmgqKH/SMbSmjxQ7QjyNqfXVc8VJcoBV2UEg+sxQD15GQofGRh2hfpwUb70VC31DR7Rq5Hdw==", + "dev": true + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "growl": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz", + "integrity": "sha1-Sy3sjZB+k9szZiTc7AGDUC+MlCg=", + "dev": true + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", + "dev": true + }, + "ignore": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", + "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", + "dev": true + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "3.0.0", + "chalk": "2.3.0", + "cli-cursor": "2.1.0", + "cli-width": "2.2.0", + "external-editor": "2.1.0", + "figures": "2.0.0", + "lodash": "4.17.4", + "mute-stream": "0.0.7", + "run-async": "2.3.0", + "rx-lite": "4.0.8", + "rx-lite-aggregates": "4.0.8", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "through": "2.3.8" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "dev": true, + "requires": { + "is-path-inside": "1.0.1" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "1.0.2" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "jade": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "dev": true, + "requires": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "dependencies": { + "commander": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", + "dev": true + }, + "mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", + "dev": true + } + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", + "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "4.0.0" + } + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2", + "type-check": "0.3.2" + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true + }, + "lru-cache": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", + "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "mimic-fn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", + "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.4.5.tgz", + "integrity": "sha1-FRdo3Sh161G8gpXpgAAm6fK7OY8=", + "dev": true, + "requires": { + "commander": "2.3.0", + "debug": "2.2.0", + "diff": "1.4.0", + "escape-string-regexp": "1.0.2", + "glob": "3.2.3", + "growl": "1.8.1", + "jade": "0.26.3", + "mkdirp": "0.5.1", + "supports-color": "1.2.0" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "0.7.1" + } + }, + "escape-string-regexp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", + "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=", + "dev": true + }, + "glob": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "integrity": "sha1-4xPusknHr/qlxHUoaw4RW1mDlGc=", + "dev": true, + "requires": { + "graceful-fs": "2.0.3", + "inherits": "2.0.3", + "minimatch": "0.2.14" + } + }, + "graceful-fs": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz", + "integrity": "sha1-fNLNsiiko/Nule+mzBQt59GhNtA=", + "dev": true + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", + "dev": true + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "dev": true, + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + }, + "supports-color": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz", + "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=", + "dev": true + } + } + }, + "moment": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", + "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "1.1.0" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "0.1.0", + "resolve-from": "1.0.1" + } + }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "2.0.1", + "signal-exit": "3.0.2" + } + }, + "rimraf": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz", + "integrity": "sha1-YrqUf6TAtDY4Oa7+zU8PutYFlyY=", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "2.1.0" + } + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "4.0.8" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + } + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "table": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "dev": true, + "requires": { + "ajv": "5.5.2", + "ajv-keywords": "2.1.1", + "chalk": "2.3.0", + "lodash": "4.17.4", + "slice-ansi": "1.0.0", + "string-width": "2.1.1" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "1.0.2" + } + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "1.1.2" + } + }, + "type-detect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", + "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, + "winston-compat": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/winston-compat/-/winston-compat-0.0.1.tgz", + "integrity": "sha1-iAn6rNP/sc5qPjfbn1SDS3QGhp0=", + "requires": { + "cycle": "1.0.3" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "0.5.1" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } +} diff --git a/test/transport-tests.js b/test/transport-tests.js index 0ce341c..2b67170 100644 --- a/test/transport-tests.js +++ b/test/transport-tests.js @@ -16,10 +16,20 @@ function sendLogItem(transport, level, message, meta, cb) { // eslint-disable-li if (semver.major(winston.version) === 2) { transport.log(level, message, meta, cb); } else { - transport.log({ + var logger = winston.createLogger({ + transports: [transport] + }); + + transport.on('logged', function () { + if (cb) { + cb(null, true); + } + }); + + logger.info({ level: level, message: message - }, cb); + }); } } From 834b346aaa159898efba4f61b02ac3365bbb4dbe Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Thu, 1 Feb 2018 11:55:24 -0700 Subject: [PATCH 12/21] version bump for rc.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 78fdc77..bf45e3b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "winston-daily-rotate-file", - "version": "2.0.0-rc.1", + "version": "2.0.0-rc.2", "description": "A transport for winston which logs to a rotating file each day.", "main": "index.js", "engines": { From 49623048f128e7686354f676eab2bc8ab1dbedc5 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Thu, 1 Feb 2018 17:14:40 -0700 Subject: [PATCH 13/21] adding triple-beam reference --- daily-rotate-file.js | 2 +- package-lock.json | 7 ++++++- package.json | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/daily-rotate-file.js b/daily-rotate-file.js index 5f10d7e..1d2988a 100644 --- a/daily-rotate-file.js +++ b/daily-rotate-file.js @@ -8,6 +8,7 @@ var semver = require('semver'); var zlib = require('zlib'); var winston = require('winston'); var compat = require('winston-compat'); +var {MESSAGE} = require('triple-beam'); var PassThrough = require('stream').PassThrough; var Transport = semver.major(winston.version) === 2 ? compat.Transport : require('winston-transport'); @@ -128,7 +129,6 @@ if (semver.major(winston.version) === 2) { } else { DailyRotateFile.prototype.normalizeQuery = compat.Transport.prototype.normalizeQuery; DailyRotateFile.prototype.log = function (info, callback) { - var MESSAGE = Symbol.for('message'); callback = callback || noop; this.logStream.write(info[MESSAGE] + this.options.eol); diff --git a/package-lock.json b/package-lock.json index 44ac40c..a76ab21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "winston-daily-rotate-file", - "version": "2.0.0-rc.1", + "version": "2.0.0-rc.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1260,6 +1260,11 @@ "os-tmpdir": "1.0.2" } }, + "triple-beam": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.1.0.tgz", + "integrity": "sha1-KsOHyMS9BL0mxh34kaYHn4WS/hA=" + }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", diff --git a/package.json b/package.json index bf45e3b..2b0382f 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "dependencies": { "file-stream-rotator": "^0.2.0", "semver": "^5.5.0", + "triple-beam": "^1.1.0", "winston-compat": "0.0.1" } } From e130267c5f0e4bc86a224012bc16d12ecdd124b1 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Thu, 1 Feb 2018 20:39:25 -0700 Subject: [PATCH 14/21] temporary fix until upstream file-stream-rotator has patch in place --- daily-rotate-file.js | 2 +- package-lock.json | 4 +--- package.json | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/daily-rotate-file.js b/daily-rotate-file.js index 1d2988a..f5739f2 100644 --- a/daily-rotate-file.js +++ b/daily-rotate-file.js @@ -8,7 +8,7 @@ var semver = require('semver'); var zlib = require('zlib'); var winston = require('winston'); var compat = require('winston-compat'); -var {MESSAGE} = require('triple-beam'); +var MESSAGE = require('triple-beam').MESSAGE; var PassThrough = require('stream').PassThrough; var Transport = semver.major(winston.version) === 2 ? compat.Transport : require('winston-transport'); diff --git a/package-lock.json b/package-lock.json index a76ab21..efdc8da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -531,9 +531,7 @@ } }, "file-stream-rotator": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.2.0.tgz", - "integrity": "sha1-nXY4MGlQ7YO9dYYmh4SxqUh+CKs=", + "version": "github:mattberther/file-stream-rotator#1d9316fddf3c0d686797bcd920bfd3d6020fd2a6", "requires": { "moment": "2.20.1" } diff --git a/package.json b/package.json index 2b0382f..1e0c112 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "rimraf": "2.5.2" }, "dependencies": { - "file-stream-rotator": "^0.2.0", + "file-stream-rotator": "github:mattberther/file-stream-rotator#filesize-check", "semver": "^5.5.0", "triple-beam": "^1.1.0", "winston-compat": "0.0.1" From 35f073337eec54584fb594f61ff76bbf570274b9 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Sat, 17 Feb 2018 16:26:11 -0700 Subject: [PATCH 15/21] incorporating file-stream-rotator fix to continue last file if below the threshold --- package-lock.json | 4 +++- package.json | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index efdc8da..6031e65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -531,7 +531,9 @@ } }, "file-stream-rotator": { - "version": "github:mattberther/file-stream-rotator#1d9316fddf3c0d686797bcd920bfd3d6020fd2a6", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.2.1.tgz", + "integrity": "sha1-DW/qGpp6uiWofP0xtuJp5E6PCvI=", "requires": { "moment": "2.20.1" } diff --git a/package.json b/package.json index 1e0c112..31fe3c1 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "rimraf": "2.5.2" }, "dependencies": { - "file-stream-rotator": "github:mattberther/file-stream-rotator#filesize-check", + "file-stream-rotator": "^0.2.1", "semver": "^5.5.0", "triple-beam": "^1.1.0", "winston-compat": "0.0.1" From 16c35868bf0f1fc6890a278ba2d223cb94e10c4c Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Thu, 1 Mar 2018 15:14:15 -0700 Subject: [PATCH 16/21] prevent bubbling of events from the underlying stream --- daily-rotate-file.js | 25 +++++++------------------ package-lock.json | 2 +- package.json | 2 +- test/transport-tests.js | 8 ++++---- 4 files changed, 13 insertions(+), 24 deletions(-) diff --git a/daily-rotate-file.js b/daily-rotate-file.js index f5739f2..587a06b 100644 --- a/daily-rotate-file.js +++ b/daily-rotate-file.js @@ -73,22 +73,6 @@ var DailyRotateFile = function (options) { max_logs: options.maxFiles }); - this.logStream.on('close', function () { - self.emit('close'); - }); - - this.logStream.on('finish', function () { - self.emit('finish'); - }); - - this.logStream.on('error', function (err) { - self.emit('error', err); - }); - - this.logStream.on('open', function (fd) { - self.emit('open', fd); - }); - this.logStream.on('rotate', function (oldFile, newFile) { self.emit('rotate', oldFile, newFile); }); @@ -137,9 +121,14 @@ if (semver.major(winston.version) === 2) { }; } -DailyRotateFile.prototype.close = function () { +DailyRotateFile.prototype.end = function (chunk, encoding, callback) { + var self = this; if (this.logStream) { - this.logStream.end(); + this.logStream.end(chunk, encoding, function () { + self.emit('finish'); + callback = callback || noop; + callback(); + }); } }; diff --git a/package-lock.json b/package-lock.json index 6031e65..6071355 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "winston-daily-rotate-file", - "version": "2.0.0-rc.2", + "version": "2.0.0-rc.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 31fe3c1..5c7066c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "winston-daily-rotate-file", - "version": "2.0.0-rc.2", + "version": "2.0.0-rc.4", "description": "A transport for winston which logs to a rotating file each day.", "main": "index.js", "engines": { diff --git a/test/transport-tests.js b/test/transport-tests.js index 2b67170..f43a954 100644 --- a/test/transport-tests.js +++ b/test/transport-tests.js @@ -119,7 +119,7 @@ describe('winston/transports/daily-rotate-file', function () { expect(logged).to.be.true; }); - this.transport.close(); + this.transport.end(); }); it('should not allow the stream to be set', function () { @@ -150,7 +150,7 @@ describe('winston/transports/daily-rotate-file', function () { }); sendLogItem(this.transport, 'info', randomString(1056)); sendLogItem(this.transport, 'info', randomString(1056)); - self.transport.close(); + self.transport.end(); }); }); @@ -175,8 +175,6 @@ describe('winston/transports/daily-rotate-file', function () { sendLogItem(this.transport, 'info', randomString(1056)); sendLogItem(this.transport, 'info', randomString(1056)); - this.transport.close(); - var self = this; this.transport.on('finish', function () { self.transport.query(function (err, results) { @@ -185,6 +183,8 @@ describe('winston/transports/daily-rotate-file', function () { done(); }); }); + + this.transport.end(); }); }); }); From c36fb29b655e028dccbed0bac298ab60d6a79267 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Fri, 2 Mar 2018 12:08:21 -0700 Subject: [PATCH 17/21] move cleanup to _final() method and update documentation relating to events --- README.md | 4 ++-- daily-rotate-file.js | 14 +++++--------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index bb9a33f..e68e599 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A transport for [winston](https://github.com/winstonjs/winston) which logs to a Starting with version 2.0.0, the transport has been refactored to leverage the the [file-stream-rotator](https://github.com/rogerc/file-stream-rotator/) module. _Some of the options in the 1.x versions of the transport have changed._ Please review the options below to identify any changes needed. ## Install -``` +``` npm install winston-daily-rotate-file ``` @@ -48,7 +48,7 @@ The DailyRotateFile transport can rotate files by minute, hour, day, month, year logger.info('Hello World!'); ``` -You can listen to the *open*, *close*, *error*, and *finish* events generated by the underlying stream. You can also listen for the *rotate* custom event. The rotate event will pass two parameters to the callback (*oldFilename*, *newFilename*). +You can listen for the *rotate* custom event. The rotate event will pass two parameters to the callback (*oldFilename*, *newFilename*). ## LICENSE MIT diff --git a/daily-rotate-file.js b/daily-rotate-file.js index 587a06b..091f011 100644 --- a/daily-rotate-file.js +++ b/daily-rotate-file.js @@ -121,15 +121,11 @@ if (semver.major(winston.version) === 2) { }; } -DailyRotateFile.prototype.end = function (chunk, encoding, callback) { - var self = this; - if (this.logStream) { - this.logStream.end(chunk, encoding, function () { - self.emit('finish'); - callback = callback || noop; - callback(); - }); - } +DailyRotateFile.prototype._final = function (callback) { + this.logStream.end(function () { + callback = callback || noop; + callback(); + }); }; DailyRotateFile.prototype.query = function (options, callback) { From 4c674e814eab7a591810376830f030464f196878 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Fri, 2 Mar 2018 12:09:17 -0700 Subject: [PATCH 18/21] version bump for @next --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6071355..5bc662a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "winston-daily-rotate-file", - "version": "2.0.0-rc.4", + "version": "2.0.0-rc.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 5c7066c..6731a50 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "winston-daily-rotate-file", - "version": "2.0.0-rc.4", + "version": "2.0.0-rc.5", "description": "A transport for winston which logs to a rotating file each day.", "main": "index.js", "engines": { From 8e82e5e1fde92861d299dedb0a93f8b91e0f0e72 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Fri, 2 Mar 2018 13:03:17 -0700 Subject: [PATCH 19/21] implementing custom method to close stream for tests --- daily-rotate-file.js | 12 +++++++----- test/transport-tests.js | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/daily-rotate-file.js b/daily-rotate-file.js index 091f011..24d23ae 100644 --- a/daily-rotate-file.js +++ b/daily-rotate-file.js @@ -121,11 +121,13 @@ if (semver.major(winston.version) === 2) { }; } -DailyRotateFile.prototype._final = function (callback) { - this.logStream.end(function () { - callback = callback || noop; - callback(); - }); +DailyRotateFile.prototype.close = function () { + var self = this; + if (this.logStream) { + this.logStream.end(function () { + self.emit('finish'); + }); + } }; DailyRotateFile.prototype.query = function (options, callback) { diff --git a/test/transport-tests.js b/test/transport-tests.js index f43a954..4839507 100644 --- a/test/transport-tests.js +++ b/test/transport-tests.js @@ -119,7 +119,7 @@ describe('winston/transports/daily-rotate-file', function () { expect(logged).to.be.true; }); - this.transport.end(); + this.transport.close(); }); it('should not allow the stream to be set', function () { @@ -150,7 +150,7 @@ describe('winston/transports/daily-rotate-file', function () { }); sendLogItem(this.transport, 'info', randomString(1056)); sendLogItem(this.transport, 'info', randomString(1056)); - self.transport.end(); + self.transport.close(); }); }); @@ -184,7 +184,7 @@ describe('winston/transports/daily-rotate-file', function () { }); }); - this.transport.end(); + this.transport.close(); }); }); }); From 3ed1b1e61e751fc53f64351011279608921b852c Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Fri, 16 Mar 2018 10:43:33 -0600 Subject: [PATCH 20/21] version bump to support 2.x release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5bc662a..35ddf47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "winston-daily-rotate-file", - "version": "2.0.0-rc.5", + "version": "2.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 6731a50..a37d48d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "winston-daily-rotate-file", - "version": "2.0.0-rc.5", + "version": "2.0.0", "description": "A transport for winston which logs to a rotating file each day.", "main": "index.js", "engines": { From 0a2bff25e44c68d0b9a8b5f36b94cff00a706890 Mon Sep 17 00:00:00 2001 From: Matt Berther Date: Fri, 16 Mar 2018 10:46:37 -0600 Subject: [PATCH 21/21] winston@next peer dependency is now rc2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a37d48d..c94e474 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ }, "homepage": "https://github.com/winstonjs/winston-daily-rotate-file#readme", "peerDependencies": { - "winston": "^2 <= 3.0.0-rc1 || ^3" + "winston": "^2 <= 3.0.0-rc2 || ^3" }, "devDependencies": { "chai": "3.5.0",