Skip to content

Commit

Permalink
Reintroduce support for logger.query (#257)
Browse files Browse the repository at this point in the history
[Normalize Query](https://github.com/winstonjs/winston-compat/blame/master/lib/transport.js#L59)

This PR adds back the query feature without winston dependencies.

#255
  • Loading branch information
dplewis authored and mattberther committed Dec 13, 2019
1 parent c8d05ec commit 10c58f0
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 0 deletions.
152 changes: 152 additions & 0 deletions daily-rotate-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,155 @@ DailyRotateFile.prototype.close = function () {
});
}
};

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 = options || {};

// limit
options.rows = options.rows || options.limit || 10;

// starting row offset
options.start = options.start || 0;

// now
options.until = options.until || new Date;
if (typeof options.until !== 'object') {
options.until = new Date(options.until);
}

// now - 24
options.from = options.from || (options.until - (24 * 60 * 60 * 1000));
if (typeof options.from !== 'object') {
options.from = new Date(options.from);
}

// 'asc' or 'desc'
options.order = options.order || 'desc';

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());
};
65 changes: 65 additions & 0 deletions test/transport-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe('winston/transports/daily-rotate-file', function () {
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 not allow invalid characters in the filename', function () {
Expand Down Expand Up @@ -194,5 +195,69 @@ describe('winston/transports/daily-rotate-file', function () {
this.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();
});
});
});
});
});
});

0 comments on commit 10c58f0

Please sign in to comment.