Skip to content

Commit 10c58f0

Browse files
dplewismattberther
authored andcommitted
Reintroduce support for logger.query (#257)
[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
1 parent c8d05ec commit 10c58f0

File tree

2 files changed

+217
-0
lines changed

2 files changed

+217
-0
lines changed

daily-rotate-file.js

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,155 @@ DailyRotateFile.prototype.close = function () {
148148
});
149149
}
150150
};
151+
152+
DailyRotateFile.prototype.query = function (options, callback) {
153+
if (typeof options === 'function') {
154+
callback = options;
155+
options = {};
156+
}
157+
158+
if (!this.options.json) {
159+
throw new Error('query() may not be used without the json option being set to true');
160+
}
161+
162+
if (!this.filename) {
163+
throw new Error('query() may not be used when initializing with a stream');
164+
}
165+
166+
var self = this;
167+
var results = [];
168+
options = options || {};
169+
170+
// limit
171+
options.rows = options.rows || options.limit || 10;
172+
173+
// starting row offset
174+
options.start = options.start || 0;
175+
176+
// now
177+
options.until = options.until || new Date;
178+
if (typeof options.until !== 'object') {
179+
options.until = new Date(options.until);
180+
}
181+
182+
// now - 24
183+
options.from = options.from || (options.until - (24 * 60 * 60 * 1000));
184+
if (typeof options.from !== 'object') {
185+
options.from = new Date(options.from);
186+
}
187+
188+
// 'asc' or 'desc'
189+
options.order = options.order || 'desc';
190+
191+
var logFiles = (function () {
192+
var fileRegex = new RegExp(self.filename.replace('%DATE%', '.*'), 'i');
193+
return fs.readdirSync(self.dirname).filter(function (file) {
194+
return path.basename(file).match(fileRegex);
195+
});
196+
})();
197+
198+
if (logFiles.length === 0 && callback) {
199+
callback(null, results);
200+
}
201+
202+
(function processLogFile(file) {
203+
if (!file) {
204+
return;
205+
}
206+
207+
var logFile = path.join(self.dirname, file);
208+
var buff = '';
209+
210+
var stream;
211+
212+
if (file.endsWith('.gz')) {
213+
stream = new PassThrough();
214+
fs.createReadStream(logFile).pipe(zlib.createGunzip()).pipe(stream);
215+
} else {
216+
stream = fs.createReadStream(logFile, {
217+
encoding: 'utf8'
218+
});
219+
}
220+
221+
stream.on('error', function (err) {
222+
if (stream.readable) {
223+
stream.destroy();
224+
}
225+
226+
if (!callback) {
227+
return;
228+
}
229+
230+
return err.code === 'ENOENT' ? callback(null, results) : callback(err);
231+
});
232+
233+
stream.on('data', function (data) {
234+
data = (buff + data).split(/\n+/);
235+
var l = data.length - 1;
236+
237+
for (var i = 0; i < l; i++) {
238+
add(data[i]);
239+
}
240+
241+
buff = data[l];
242+
});
243+
244+
stream.on('end', function () {
245+
if (buff) {
246+
add(buff, true);
247+
}
248+
249+
if (logFiles.length) {
250+
processLogFile(logFiles.shift());
251+
} else if (callback) {
252+
results.sort(function (a, b) {
253+
var d1 = new Date(a.timestamp).getTime();
254+
var d2 = new Date(b.timestamp).getTime();
255+
256+
return d1 > d2 ? 1 : d1 < d2 ? -1 : 0;
257+
});
258+
259+
if (options.order === 'desc') {
260+
results = results.reverse();
261+
}
262+
263+
var start = options.start || 0;
264+
var limit = options.limit || results.length;
265+
266+
results = results.slice(start, start + limit);
267+
268+
if (options.fields) {
269+
results = results.map(function (log) {
270+
var obj = {};
271+
options.fields.forEach(function (key) {
272+
obj[key] = log[key];
273+
});
274+
return obj;
275+
});
276+
}
277+
278+
callback(null, results);
279+
}
280+
});
281+
282+
function add(buff, attempt) {
283+
try {
284+
var log = JSON.parse(buff);
285+
if (!log || typeof log !== 'object') {
286+
return;
287+
}
288+
289+
var time = new Date(log.timestamp);
290+
if ((options.from && time < options.from) || (options.until && time > options.until)) {
291+
return;
292+
}
293+
294+
results.push(log);
295+
} catch (e) {
296+
if (!attempt) {
297+
stream.emit('error', e);
298+
}
299+
}
300+
}
301+
})(logFiles.shift());
302+
};

test/transport-tests.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ describe('winston/transports/daily-rotate-file', function () {
4141
var transport = new DailyRotateFile({stream: new MemoryStream()});
4242
expect(transport).to.be.instanceOf(DailyRotateFile);
4343
expect(transport).to.respondTo('log');
44+
expect(transport).to.respondTo('query');
4445
});
4546

4647
it('should not allow invalid characters in the filename', function () {
@@ -194,5 +195,69 @@ describe('winston/transports/daily-rotate-file', function () {
194195
this.transport.close();
195196
});
196197
});
198+
199+
describe('query', function () {
200+
it('should call callback when no files are present', function () {
201+
this.transport.query(function (err, results) {
202+
expect(results).to.not.be.null;
203+
expect(results.length).to.equal(0);
204+
});
205+
});
206+
207+
it('should raise error when calling with stream', function () {
208+
expect(function () {
209+
var transport = new DailyRotateFile({stream: new MemoryStream()});
210+
transport.query(null);
211+
}).to.throw();
212+
});
213+
214+
it('should raise error when calling with json set to false', function () {
215+
expect(function () {
216+
var opts = Object.assign({}, options);
217+
opts.json = false;
218+
var transport = new DailyRotateFile(opts);
219+
transport.query(null);
220+
}).to.throw();
221+
});
222+
223+
it('should return log entries that match the query', function (done) {
224+
sendLogItem(this.transport, 'info', randomString(1056));
225+
sendLogItem(this.transport, 'info', randomString(1056));
226+
sendLogItem(this.transport, 'info', randomString(1056));
227+
sendLogItem(this.transport, 'info', randomString(1056));
228+
229+
var self = this;
230+
this.transport.on('finish', function () {
231+
self.transport.query(function (err, results) {
232+
expect(results).to.not.be.null;
233+
expect(results.length).to.equal(4);
234+
done();
235+
});
236+
});
237+
238+
this.transport.close();
239+
});
240+
241+
it('should search within archived files', function (done) {
242+
var opts = Object.assign({}, options);
243+
opts.zippedArchive = true;
244+
opts.maxSize = '1k';
245+
246+
this.transport = new DailyRotateFile(opts);
247+
248+
sendLogItem(this.transport, 'info', randomString(1056));
249+
sendLogItem(this.transport, 'info', randomString(1056));
250+
251+
var self = this;
252+
253+
self.transport.on('archive', function () {
254+
self.transport.query(function (err, results) {
255+
expect(results).to.not.be.null;
256+
expect(results.length).to.equal(2);
257+
done();
258+
});
259+
});
260+
});
261+
});
197262
});
198263
});

0 commit comments

Comments
 (0)