-
Notifications
You must be signed in to change notification settings - Fork 0
/
reporter.js
95 lines (82 loc) · 3.36 KB
/
reporter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/**
* Configures one or more counters for regular expressions, and reports the collected statistics at the configured
* interval.
*/
'use strict';
const moment = require('moment');
class Reporter {
constructor(reports, schedule, logger) {
this.reports = reports;
this.schedule = schedule;
this.logger = logger;
this.lastRun = Date.now();
this.counts = {};
if (schedule.interval) {
// The first run might use an interval that is a lot smaller than the configured interval; see below
this._schedule();
}
else {
this.logger.warn('No reporting interval in configuration; not enabling reporter ' + schedule.name);
}
}
/**
* Compares the given message to the known regular expressions, and increases the report's counter if matched.
*
* @param msg The message to compare against the configured patterns
*/
message(msg) {
this.reports.forEach(report => {
if (report.include.some(p => p.test(msg)) && !(report.exclude || []).some(p => p.test(msg))) {
this.counts[report.name] = (this.counts[report.name] || 0) + 1;
}
});
}
/**
* Schedules the next report.
*
* @private
*/
_schedule() {
setTimeout(() => this._report(), this._minimizeInterval(this.schedule.interval));
}
/**
* Decreases the given `interval` to match the last possible time that coincides with the unit of the given
* interval, if any.
*
* Like if the interval is an exact number of days, this yields the milliseconds until some next day (local time),
* and when called just before midnight the returned interval might be quite small. Likewise this detects multiples
* of hours, 30 minutes, 15 minutes, 10 minutes, 5 minutes, 1 minute, 30 seconds, 15 seconds, 10 seconds, 5 seconds
* and a single second. If no unit is detected, this just returns the given interval. But as, e.g., 11 minutes will
* be matched as a multiple of 1 minute, and 62 seconds as a multiple of 1 second, such will only happen when
* explicitly passing an interval that cannot be rounded to a second.
*
* @param interval the maximum number of milliseconds until the expected time
* @private
*/
_minimizeInterval(interval) {
const unit = [24 * 3600, 3600, 30 * 60, 15 * 60, 10 * 60, 5 * 60, 60, 30, 15, 10, 5, 1].find(s => interval % (s * 1000) === 0) * 1000;
if (!unit) {
return interval;
}
const utc = new Date();
// Ensure daily reports start at midnight in the local timezone
const now = utc.getTime() - (utc.getTimezoneOffset() * 60 * 1000);
const next = Math.floor((now + interval) / unit) * unit;
return next - now;
}
/**
* Reports the collected statistics at the configured log level, and schedules a new report.
*
* @private
*/
_report() {
const msg = this.schedule.name + ' since ' + moment(this.lastRun).format('YYYY-MM-DD HH:mm')
+ ':\n'
+ this.reports.map(report => '\u2022 ' + report.name + ": " + (this.counts[report.name] || 0)).join('\n');
this.logger.log(this.schedule.level || 'info', msg);
this.lastRun = Date.now();
this.counts = {};
this._schedule();
}
}
module.exports = Reporter;