winston-daily-rotate-file
Advanced tools
Comparing version 3.10.0 to 4.0.0
@@ -7,10 +7,7 @@ 'use strict'; | ||
var util = require('util'); | ||
var semver = require('semver'); | ||
var zlib = require('zlib'); | ||
var hash = require('object-hash'); | ||
var winston = require('winston'); | ||
var compat = require('winston-compat'); | ||
var MESSAGE = require('triple-beam').MESSAGE; | ||
var PassThrough = require('stream').PassThrough; | ||
var Transport = semver.major(winston.version) === 2 ? compat.Transport : require('winston-transport'); | ||
var Transport = require('winston-transport'); | ||
@@ -93,3 +90,5 @@ var loggerDefaults = { | ||
audit_file: options.auditFile ? options.auditFile : path.join(self.dirname, '.' + hash(options) + '-audit.json'), | ||
file_options: options.options ? options.options : {flags: 'a'} | ||
file_options: options.options ? options.options : {flags: 'a'}, | ||
utc: options.utc ? options.utc : false, | ||
extension: options.extension ? options.extension : '' | ||
}); | ||
@@ -105,2 +104,6 @@ | ||
this.logStream.on('logRemoved', function (params) { | ||
self.emit('logRemoved', params.name); | ||
}); | ||
if (options.zippedArchive) { | ||
@@ -133,26 +136,10 @@ this.logStream.on('rotate', function (oldFile) { | ||
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 | ||
}); | ||
DailyRotateFile.prototype.log = function (info, callback) { | ||
callback = callback || noop; | ||
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) { | ||
callback = callback || noop; | ||
this.logStream.write(info[MESSAGE] + this.options.eol); | ||
this.emit('logged', info); | ||
callback(null, true); | ||
}; | ||
this.logStream.write(info[MESSAGE] + this.options.eol); | ||
this.emit('logged', info); | ||
callback(null, true); | ||
}; | ||
} | ||
DailyRotateFile.prototype.close = function () { | ||
@@ -166,132 +153,1 @@ var self = this; | ||
}; | ||
DailyRotateFile.prototype.query = function (options, callback) { | ||
if (typeof options === 'function') { | ||
callback = options; | ||
options = {}; | ||
} | ||
if (!this.options.json) { | ||
throw new Error('query() may not be used without the json option being set to true'); | ||
} | ||
if (!this.filename) { | ||
throw new Error('query() may not be used when initializing with a stream'); | ||
} | ||
var self = this; | ||
var results = []; | ||
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; | ||
if (file.endsWith('.gz')) { | ||
stream = new PassThrough(); | ||
fs.createReadStream(logFile).pipe(zlib.createGunzip()).pipe(stream); | ||
} else { | ||
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++) { | ||
add(data[i]); | ||
} | ||
buff = data[l]; | ||
}); | ||
stream.on('end', function () { | ||
if (buff) { | ||
add(buff, true); | ||
} | ||
if (logFiles.length) { | ||
processLogFile(logFiles.shift()); | ||
} else if (callback) { | ||
results.sort(function (a, b) { | ||
var d1 = new Date(a.timestamp).getTime(); | ||
var d2 = new Date(b.timestamp).getTime(); | ||
return d1 > d2 ? 1 : d1 < d2 ? -1 : 0; | ||
}); | ||
if (options.order === 'desc') { | ||
results = results.reverse(); | ||
} | ||
var start = options.start || 0; | ||
var limit = options.limit || results.length; | ||
results = results.slice(start, start + limit); | ||
if (options.fields) { | ||
results = results.map(function (log) { | ||
var obj = {}; | ||
options.fields.forEach(function (key) { | ||
obj[key] = log[key]; | ||
}); | ||
return obj; | ||
}); | ||
} | ||
callback(null, results); | ||
} | ||
}); | ||
function add(buff, attempt) { | ||
try { | ||
var log = JSON.parse(buff); | ||
if (!log || typeof log !== 'object') { | ||
return; | ||
} | ||
var time = new Date(log.timestamp); | ||
if ((options.from && time < options.from) || (options.until && time > options.until)) { | ||
return; | ||
} | ||
results.push(log); | ||
} catch (e) { | ||
if (!attempt) { | ||
stream.emit('error', e); | ||
} | ||
} | ||
} | ||
})(logFiles.shift()); | ||
}; |
@@ -50,5 +50,5 @@ declare module "winston-daily-rotate-file" { | ||
/** | ||
* A string representing the name of the name of the audit file. (default: './hash-audit.json' ) | ||
* A string representing the name of the name of the audit file. (default: './hash-audit.json') | ||
*/ | ||
auditFile?: string | ||
auditFile?: string; | ||
@@ -58,3 +58,13 @@ /** | ||
*/ | ||
frequency?: string | ||
frequency?: string; | ||
/** | ||
* A boolean whether or not to generate file name from "datePattern" in UTC format. (default: false) | ||
*/ | ||
utc?: boolean; | ||
/** | ||
* A string representing an extension to be added to the filename, if not included in the filename property. (default: '') | ||
*/ | ||
extension?: string; | ||
} | ||
@@ -61,0 +71,0 @@ } |
{ | ||
"name": "winston-daily-rotate-file", | ||
"version": "3.10.0", | ||
"version": "4.0.0", | ||
"description": "A transport for winston which logs to a rotating file each day.", | ||
@@ -8,3 +8,3 @@ "main": "index.js", | ||
"engines": { | ||
"node": ">=6" | ||
"node": ">=8" | ||
}, | ||
@@ -33,3 +33,3 @@ "scripts": { | ||
"peerDependencies": { | ||
"winston": "^2 || ^3" | ||
"winston": "^3" | ||
}, | ||
@@ -45,9 +45,7 @@ "devDependencies": { | ||
"dependencies": { | ||
"file-stream-rotator": "^0.4.1", | ||
"file-stream-rotator": "^0.5.4", | ||
"object-hash": "^1.3.0", | ||
"semver": "^6.2.0", | ||
"triple-beam": "^1.3.0", | ||
"winston-compat": "^0.1.4", | ||
"winston-transport": "^4.2.0" | ||
} | ||
} |
@@ -9,2 +9,5 @@ # winston-daily-rotate-file | ||
## Compatibility | ||
Please note that if you are using `winston@2`, you will need to use `winston-daily-rotate-file@3`. `winston-daily-rotate-file@4` removed support for `winston@2`. | ||
## Install | ||
@@ -19,4 +22,4 @@ ``` | ||
* **frequency:** A string representing the frequency of rotation. This is useful if you want to have timed rotations, as opposed to rotations that happen at specific moments in time. Valid values are '#m' or '#h' (e.g., '5m' or '3h'). Leaving this null relies on `datePattern` for the rotation times. (default: null) | ||
* **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') | ||
* **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%) | ||
@@ -29,2 +32,4 @@ * **dirname:** The directory name to save log files to. (default: '.') | ||
* **auditFile**: A string representing the name of the name of the audit file. This can be used to override the default filename which is generated by computing a hash of the options object. (default: '.<optionsHash>.json') | ||
* **utc**: Use UTC time for date in filename. (default: false) | ||
* **extension**: File extension to be appended to the filename. (default: '') | ||
@@ -57,4 +62,9 @@ ## Usage | ||
This transport emits three custom events: *new*, *rotate*, and *archive*. You can listen for the *new* custom event, which is fired when a new log file is created. The new event will pass one parameter to the callback (*newFilename*). You can listen for the *rotate* custom event, which is fired when the log file is rotated. The rotate event will pass two parameters to the callback (*oldFilename*, *newFilename*). You can also listen for the *archive* custom event, which is fired when the log file is archived. The archive event will pass one parameter to the callback (*zipFilename*). | ||
This transport emits the following custom events: | ||
* **new**: fired when a new log file is created. This event will pass one parameter to the callback (*newFilename*). | ||
* **rotate**: fired when the log file is rotated. This event will pass two parameters to the callback (*oldFilename*, *newFilename*). | ||
* **archive**: fired when the log file is archived. This event will pass one parameter to the callback (*zipFilename*). | ||
* **logRemoved**: fired when a log file is removed from the file system. This event will pass one parameter to the callback (*removedFilename*). | ||
## LICENSE | ||
@@ -61,0 +71,0 @@ MIT |
@@ -9,3 +9,2 @@ /* eslint-disable max-nested-callbacks,no-unused-expressions,handle-callback-err */ | ||
var moment = require('moment'); | ||
var semver = require('semver'); | ||
var winston = require('winston'); | ||
@@ -17,20 +16,16 @@ var MemoryStream = require('./memory-stream'); | ||
function sendLogItem(transport, level, message, meta, cb) { // eslint-disable-line max-params | ||
if (semver.major(winston.version) === 2) { | ||
transport.log(level, message, meta, cb); | ||
} else { | ||
var logger = winston.createLogger({ | ||
transports: [transport] | ||
}); | ||
var logger = winston.createLogger({ | ||
transports: [transport] | ||
}); | ||
transport.on('logged', function () { | ||
if (cb) { | ||
cb(null, true); | ||
} | ||
}); | ||
transport.on('logged', function () { | ||
if (cb) { | ||
cb(null, true); | ||
} | ||
}); | ||
logger.info({ | ||
level: level, | ||
message: message | ||
}); | ||
} | ||
logger.info({ | ||
level: level, | ||
message: message | ||
}); | ||
} | ||
@@ -51,3 +46,2 @@ | ||
expect(transport).to.respondTo('log'); | ||
expect(transport).to.respondTo('query'); | ||
}); | ||
@@ -111,9 +105,11 @@ | ||
var logDir = path.join(__dirname, 'logs'); | ||
var now = moment().format('YYYY-MM-DD-HH'); | ||
var filename = path.join(logDir, 'application-' + now + '.log'); | ||
var now = moment().utc().format('YYYY-MM-DD-HH'); | ||
var filename = path.join(logDir, 'application-' + now + '.testlog'); | ||
var options = { | ||
json: true, | ||
dirname: logDir, | ||
filename: 'application-%DATE%.log', | ||
datePattern: 'YYYY-MM-DD-HH' | ||
filename: 'application-%DATE%', | ||
datePattern: 'YYYY-MM-DD-HH', | ||
utc: true, | ||
extension: '.testlog' | ||
}; | ||
@@ -167,5 +163,21 @@ | ||
it('should raise the logRemoved event when pruning old log files', function (done) { | ||
var opts = Object.assign({}, options); | ||
opts.maxSize = '1k'; | ||
opts.maxFiles = 1; | ||
this.transport = new DailyRotateFile(opts); | ||
this.transport.on('logRemoved', function (removedFilename) { | ||
expect(removedFilename).to.equal(filename); | ||
done(); | ||
}); | ||
sendLogItem(this.transport, 'info', randomString(1056)); | ||
sendLogItem(this.transport, 'info', randomString(1056)); | ||
this.transport.close(); | ||
}); | ||
describe('when setting zippedArchive', function () { | ||
it('should archive the log after rotating', function (done) { | ||
var self = this; | ||
var opts = Object.assign({}, options); | ||
@@ -187,70 +199,6 @@ opts.zippedArchive = true; | ||
sendLogItem(this.transport, '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 DailyRotateFile({stream: new MemoryStream()}); | ||
transport.query(null); | ||
}).to.throw(); | ||
}); | ||
it('should raise error when calling with json set to false', function () { | ||
expect(function () { | ||
var opts = Object.assign({}, options); | ||
opts.json = false; | ||
var transport = new DailyRotateFile(opts); | ||
transport.query(null); | ||
}).to.throw(); | ||
}); | ||
it('should return log entries that match the query', function (done) { | ||
sendLogItem(this.transport, 'info', randomString(1056)); | ||
sendLogItem(this.transport, 'info', randomString(1056)); | ||
sendLogItem(this.transport, 'info', randomString(1056)); | ||
sendLogItem(this.transport, 'info', randomString(1056)); | ||
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(); | ||
}); | ||
}); | ||
this.transport.close(); | ||
}); | ||
it('should search within archived files', function (done) { | ||
var opts = Object.assign({}, options); | ||
opts.zippedArchive = true; | ||
opts.maxSize = '1k'; | ||
this.transport = new DailyRotateFile(opts); | ||
sendLogItem(this.transport, 'info', randomString(1056)); | ||
sendLogItem(this.transport, 'info', randomString(1056)); | ||
var self = this; | ||
self.transport.on('archive', function () { | ||
self.transport.query(function (err, results) { | ||
expect(results).to.not.be.null; | ||
expect(results.length).to.equal(2); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
5
78
24914
433
+ Addedfile-stream-rotator@0.5.7(transitive)
- Removedsemver@^6.2.0
- Removedwinston-compat@^0.1.4
- Removedcolors@1.4.0(transitive)
- Removedcycle@1.0.3(transitive)
- Removedfast-safe-stringify@2.1.1(transitive)
- Removedfecha@2.3.3(transitive)
- Removedfile-stream-rotator@0.4.1(transitive)
- Removedlogform@1.10.0(transitive)
- Removedsemver@6.3.1(transitive)
- Removedwinston-compat@0.1.5(transitive)
Updatedfile-stream-rotator@^0.5.4