@emartech/aggregate-metric-logger 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc.js ADDED
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ env: {
5
+ es6: true,
6
+ browser: true,
7
+ node: true
8
+ },
9
+ parserOptions: {
10
+ ecmaVersion: 2018
11
+ },
12
+ extends: ['eslint:recommended', 'plugin:jest/recommended', 'emarsys'],
13
+ rules: {
14
+ 'curly': ['error', 'multi-line']
15
+ },
16
+ overrides: [
17
+ {
18
+ files: ['*.spec.js'],
19
+ env: {
20
+ jest: true
21
+ }
22
+ }
23
+ ]
24
+ };
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ v16.14.0
package/LICENSE.txt ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2022 Emarsys
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # aggregate-metric-logger
2
+ aggregate metrics in memory and log them every minute
3
+
4
+ ## Setup
5
+
6
+ Set `METRIC_LOGGER_ENABLED` env variable to `true` for aggregate-metric-logger to
7
+ start collecting and logging measuremnts.
8
+
9
+ To change the default `aggregate-metric-logger` namespace used for logs, set
10
+ `METRIC_LOGGER_NAMESPACE` to a custom value.
11
+
12
+ ## Usage
13
+
14
+ The package exposes similar logging methods as the `@emartech/json-logger` package:
15
+ `trace`, `debug`, `info`, `warn`, `error` and `fatal`.
16
+
17
+ When these are called, it will aggregate logs with the same parameters withing a minute, add
18
+ an extra `count` property to the logs and log them at the end of the minute with
19
+ `@emartech/json-logger`.
20
+
21
+ ```js
22
+ const metrictLogger = require('aggregate-metric-logger')
23
+
24
+ metricLogger.warn('etwas-went-wrong', { customer_id: 11 });
25
+ metricLogger.warn('etwas-went-wrong', { customer_id: 11 });
26
+
27
+ // will aggrete these to a single warning log
28
+ ```
29
+
30
+ ### Measure values
31
+
32
+ If you want aggregate measuremts about something you need to simply call the `measure` method
33
+ for each value:
34
+
35
+ ```js
36
+ const metrictLogger = require('aggregate-metric-logger')
37
+
38
+ metricLogger.measure('thing-to-measure', 14)
39
+ metricLogger.measure('thing-to-measure', 20)
40
+ ```
41
+
42
+ Every minute metric logger will log `min`, `max`, `sum`, `count` and `average` aggregated metrics
43
+ for each measured tags. (Uses `@emartech/json-logger` behind the scenes for logging).
44
+
45
+ ### Measuring durations
46
+
47
+ For duration measuremts, there are two convinience methods: `start`, and `stop`;
48
+
49
+ You start the measurement with `start`, the counted value for aggregation will be the duration
50
+ between this `start` and the matcing `stop` call;
51
+
52
+ ```js
53
+ function exampleIOHeavyFunction() {
54
+ const measurementId = metricLogger.start('tag-for-the-measuement');
55
+ const result = db.findItems();
56
+ metricLogger.stop(measurementId);
57
+ return result;
58
+ }
59
+ ```
60
+
61
+ ### Group by extra parameters
62
+
63
+ If you want to group by additional parameters inside a tag, you can pass down these extra
64
+ params in an object to `count` and `start` as the last optional argument:
65
+
66
+ ```js
67
+ metricLogger.count('thing-to-count', 14, { customer_id: 12 });
68
+
69
+ const measurementId = metricLogger.start(
70
+ 'tag-for-the-measuement',
71
+ { event_type: 'nyul', account: 3 }
72
+ );
73
+ ```
74
+
75
+ A separete log entry will be created for each seen value combinations seen in the extra params
76
+ and these extra params will appended to the log entry.
77
+
78
+ Be very carefull when using extra params, and only include information you really need.
79
+ If you include too many parameters, you could loose the advantage you gain by aggregating your logs
80
+ with this package.
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@emartech/aggregate-metric-logger",
3
+ "version": "1.0.0",
4
+ "description": "aggregate metrics in memory for logging",
5
+ "main": "src/index.js",
6
+ "scripts": {
7
+ "test": "npm run lint && npm run test:once",
8
+ "lint": "eslint src/",
9
+ "test:once": "jest",
10
+ "test:watch": "jest --watch --bail",
11
+ "semantic-release": "CI=true semantic-release"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/emartech/aggregate-metric-logger.git"
16
+ },
17
+ "keywords": [
18
+ "metric",
19
+ "logger",
20
+ "aggregate"
21
+ ],
22
+ "author": "RTI team",
23
+ "license": "MIT",
24
+ "bugs": {
25
+ "url": "https://github.com/emartech/aggregate-metric-logger/issues"
26
+ },
27
+ "homepage": "https://github.com/emartech/aggregate-metric-logger#readme",
28
+ "dependencies": {
29
+ "@emartech/json-logger": "^3.1.0",
30
+ "date-fns": "^2.3.0",
31
+ "json-stable-stringify": "^1.0.1",
32
+ "lodash": "^4.17.15",
33
+ "uuid": "8.3.2"
34
+ },
35
+ "devDependencies": {
36
+ "@sinonjs/fake-timers": "9.1.1",
37
+ "@types/jest": "27.4.1",
38
+ "eslint": "^8.12.0",
39
+ "eslint-config-emarsys": "^5.1.0",
40
+ "eslint-plugin-jest": "^26.1.3",
41
+ "jest": "27.5.1",
42
+ "semantic-release": "^19.0.2"
43
+ }
44
+ }
package/repo-info.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "is_in_production": true,
3
+ "is_scannable": true,
4
+ "is_critical": true,
5
+ "contact": "g-gsuite-rti@emarsys.com",
6
+ "hosted": null
7
+ }
package/src/index.js ADDED
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ const metricLoggerFactory = require('./metric-logger');
4
+
5
+ const enabled = process.env.METRIC_LOGGER_ENABLED === 'true';
6
+ const inProgressMeasurementWarningLimit =
7
+ parseInt(process.env.METRIC_LOGGER_IN_PROGRESS_MEASUREMENT_WARNING_LIMIT, 10) || 10000;
8
+
9
+ const exposedInstance = metricLoggerFactory({
10
+ enabled,
11
+ namespace: process.env.METRIC_LOGGER_NAMESPACE || 'aggregate-metric-logger',
12
+ inProgressMeasurementWarningLimit
13
+ });
14
+ const exposedFactoryInstanceHybrid = namespace => metricLoggerFactory({ enabled, namespace });
15
+
16
+ exposedFactoryInstanceHybrid.setThresholds = exposedInstance.setThresholds;
17
+ exposedFactoryInstanceHybrid.measure = exposedInstance.measure;
18
+ exposedFactoryInstanceHybrid.count = exposedInstance.count;
19
+ exposedFactoryInstanceHybrid.trace = exposedInstance.trace;
20
+ exposedFactoryInstanceHybrid.debug = exposedInstance.debug;
21
+ exposedFactoryInstanceHybrid.info = exposedInstance.info;
22
+ exposedFactoryInstanceHybrid.warn = exposedInstance.warn;
23
+ exposedFactoryInstanceHybrid.error = exposedInstance.error;
24
+ exposedFactoryInstanceHybrid.fatal = exposedInstance.fatal;
25
+ exposedFactoryInstanceHybrid.start = exposedInstance.start;
26
+ exposedFactoryInstanceHybrid.stop = exposedInstance.stop;
27
+ exposedFactoryInstanceHybrid.cancel = exposedInstance.cancel;
28
+
29
+ module.exports = exposedFactoryInstanceHybrid;
@@ -0,0 +1,145 @@
1
+ 'use strict';
2
+
3
+ const { v4: uuid } = require('uuid');
4
+ const loggerFactory = require('@emartech/json-logger');
5
+ const { getSeconds, startOfMinute, addSeconds } = require('date-fns');
6
+ const { omit, pick, fromPairs, toPairs } = require('lodash');
7
+ const stringify = require('json-stable-stringify');
8
+ const util = require('util');
9
+
10
+ module.exports = ({
11
+ enabled = true,
12
+ namespace = 'aggregate-metric-logger',
13
+ inProgressMeasurementWarningLimit = 10000
14
+ } = {}) => {
15
+ const logger = loggerFactory(namespace);
16
+
17
+ const thresholdsByTag = {};
18
+ let measurements = {};
19
+ let metrics = {};
20
+ let initialized = false;
21
+
22
+ const flush = () => {
23
+ logMetrics();
24
+ const inProgressMeasurementCount = Object.keys(measurements).length;
25
+ if (inProgressMeasurementCount > inProgressMeasurementWarningLimit) {
26
+ logger.warn('too-many-in-progress-metric-log-measurements', { count: inProgressMeasurementCount });
27
+ }
28
+ metrics = {};
29
+ setupNextFlush();
30
+ };
31
+
32
+ const setupNextFlush = () => {
33
+ const secondsInCurrentMinute = getSeconds(new Date());
34
+ const flushOffset = secondsInCurrentMinute >= 30 ? 90 : 30;
35
+ const targetFlushtime = addSeconds(startOfMinute(new Date()), flushOffset);
36
+ const timeout = targetFlushtime - Date.now();
37
+ setTimeout(flush, timeout);
38
+ };
39
+
40
+ const incrementThresholdCount = (tag, value, countsSoFar) => {
41
+ const thresholds = thresholdsByTag[tag];
42
+ if (!thresholds) return countsSoFar;
43
+
44
+ return fromPairs(thresholds.map(threshold => {
45
+ const previousCount = countsSoFar[threshold] || 0;
46
+ const newCount = value > threshold ? previousCount + 1 : previousCount;
47
+ return [threshold, newCount];
48
+ }));
49
+ };
50
+
51
+ const addMeasurement = ({ tag, value, params, method = 'info', countOnly = false }) => {
52
+ if (!enabled) return;
53
+
54
+ if (!initialized) {
55
+ setupNextFlush();
56
+ initialized = true;
57
+ }
58
+
59
+ const key = `${tag}_${stringify(params)}`;
60
+ if (!metrics[key]) {
61
+ const metricForKey = { method, tag, ...params, count: 1 };
62
+ if (!countOnly) {
63
+ metricForKey.min = metricForKey.max = metricForKey.sum = metricForKey.average = value;
64
+ metricForKey.thresholdCounts = incrementThresholdCount(tag, value, {});
65
+ }
66
+ metrics[key] = metricForKey;
67
+ } else {
68
+ const metricsSoFar = metrics[key];
69
+ metrics[key].count = metricsSoFar.count + 1;
70
+ if (!countOnly) {
71
+ metrics[key].min = Math.min(metricsSoFar.min, value);
72
+ metrics[key].max = Math.max(metricsSoFar.max, value);
73
+ metrics[key].sum = metricsSoFar.sum + value;
74
+ metrics[key].average = metrics[key].sum / metrics[key].count;
75
+ metrics[key].thresholdCounts = incrementThresholdCount(
76
+ tag,
77
+ value,
78
+ metricsSoFar.thresholdCounts
79
+ );
80
+ }
81
+ }
82
+ };
83
+
84
+ const logMetrics = () => {
85
+ Object.values(metrics).forEach(metrics => {
86
+ const params = {
87
+ is_metric: true,
88
+ ...omit(metrics, ['tag', 'method', 'thresholdCounts']),
89
+ ...fromPairs(
90
+ toPairs(metrics.thresholdCounts).map(([limit, count]) => [`above_${limit}`, count])
91
+ )
92
+ };
93
+ logger[metrics.method](metrics.tag, params);
94
+ });
95
+ };
96
+
97
+ return {
98
+ setThresholds(tag, thresholds) {
99
+ thresholdsByTag[tag] = thresholds;
100
+ },
101
+ measure(tag, value, params = {}) {
102
+ addMeasurement({ tag, value, params });
103
+ },
104
+ count: util.deprecate(
105
+ (tag, value, params = {}) => addMeasurement({ tag, value, params }),
106
+ 'count method is deprecated, use measure instead'
107
+ ),
108
+ trace(tag, params = {}) {
109
+ addMeasurement({ method: 'trace', tag, params, value: 1, countOnly: true });
110
+ },
111
+ debug(tag, params = {}) {
112
+ addMeasurement({ method: 'debug', tag, params, value: 1, countOnly: true });
113
+ },
114
+ info(tag, params = {}) {
115
+ addMeasurement({ method: 'info', tag, params, value: 1, countOnly: true });
116
+ },
117
+ warn(tag, params = {}) {
118
+ addMeasurement({ method: 'warn', tag, params, value: 1, countOnly: true });
119
+ },
120
+ error(tag, params = {}) {
121
+ addMeasurement({ method: 'error', tag, params, value: 1, countOnly: true });
122
+ },
123
+ fatal(tag, params = {}) {
124
+ addMeasurement({ method: 'fatal', tag, params, value: 1, countOnly: true });
125
+ },
126
+ start(tag, params = {}) {
127
+ const id = uuid();
128
+ measurements[id] = { tag, params, start: Date.now() };
129
+ return id;
130
+ },
131
+ stop(measurementId) {
132
+ const measurement = measurements[measurementId];
133
+ if (!measurement) return;
134
+
135
+ addMeasurement({
136
+ ...pick(measurement, ['tag', 'params']),
137
+ value: Date.now() - measurement.start
138
+ });
139
+ delete measurements[measurementId];
140
+ },
141
+ cancel(measurementId) {
142
+ delete measurements[measurementId];
143
+ }
144
+ };
145
+ };
@@ -0,0 +1,288 @@
1
+ 'use strict';
2
+
3
+ const metricLoggerFactory = require('./metric-logger');
4
+ const Logger = require('@emartech/json-logger').Logger;
5
+ const fakeTimers = require('@sinonjs/fake-timers');
6
+
7
+ jest.spyOn(Logger.prototype, 'info');
8
+ let metricLogger;
9
+ let clock;
10
+ beforeEach(() => {
11
+ jest.resetAllMocks();
12
+ metricLogger = metricLoggerFactory();
13
+ clock = fakeTimers.install();
14
+ });
15
+ afterEach(() => clock.uninstall());
16
+
17
+ describe('metricLogger', () => {
18
+ describe('measure', () => {
19
+ it('should log a single measurement at the next flush period', () => {
20
+ metricLogger.measure('elfogyasztot-tap', 4);
21
+
22
+ clock.tick(60 * 1000 + 1);
23
+ expect(Logger.prototype.info).toBeCalledWith('elfogyasztot-tap', {
24
+ min: 4,
25
+ max: 4,
26
+ count: 1,
27
+ sum: 4,
28
+ average: 4,
29
+ is_metric: true
30
+ });
31
+ });
32
+
33
+ it('should aggregate measurements withing a flush period', () => {
34
+ metricLogger.measure('elfogyasztot-tap', 4);
35
+ metricLogger.measure('elfogyasztot-tap', 8);
36
+ metricLogger.measure('kacsa-cucc', 18);
37
+
38
+ clock.tick(60 * 1000 + 1);
39
+ expect(Logger.prototype.info).toBeCalledWith('elfogyasztot-tap', {
40
+ min: 4,
41
+ max: 8,
42
+ count: 2,
43
+ sum: 12,
44
+ average: 6,
45
+ is_metric: true
46
+ });
47
+ expect(Logger.prototype.info).toBeCalledWith('kacsa-cucc', {
48
+ min: 18,
49
+ max: 18,
50
+ count: 1,
51
+ sum: 18,
52
+ average: 18,
53
+ is_metric: true
54
+ });
55
+ });
56
+
57
+ it('should log counts for each combination of extra params provedid within a tag', () => {
58
+ metricLogger.measure('elfogyasztot-tap', 4, { emberke: 'bela' });
59
+ metricLogger.measure('elfogyasztot-tap', 8, { emberke: 'bela' });
60
+ metricLogger.measure('elfogyasztot-tap', 18, { emberke: 'jano' });
61
+
62
+ clock.tick(60 * 1000 + 1);
63
+ expect(Logger.prototype.info).toBeCalledWith('elfogyasztot-tap', {
64
+ min: 4,
65
+ max: 8,
66
+ count: 2,
67
+ sum: 12,
68
+ average: 6,
69
+ emberke: 'bela',
70
+ is_metric: true
71
+ });
72
+ expect(Logger.prototype.info).toBeCalledWith('elfogyasztot-tap', {
73
+ min: 18,
74
+ max: 18,
75
+ count: 1,
76
+ sum: 18,
77
+ average: 18,
78
+ emberke: 'jano',
79
+ is_metric: true
80
+ });
81
+ });
82
+
83
+ it('should group logs with same tag and deeply equal params together', () => {
84
+ metricLogger.measure('elfogyasztot-tap', 4, { a: 1, b: 2 });
85
+ metricLogger.measure('elfogyasztot-tap', 8, { b: 2, a: 1 });
86
+
87
+ clock.tick(60 * 1000 + 1);
88
+ expect(Logger.prototype.info).toBeCalledWith('elfogyasztot-tap', {
89
+ min: 4,
90
+ max: 8,
91
+ count: 2,
92
+ sum: 12,
93
+ average: 6,
94
+ a: 1,
95
+ b: 2,
96
+ is_metric: true
97
+ });
98
+ });
99
+
100
+ it('should count event distribution within specified thresholds', () => {
101
+ metricLogger.setThresholds('etwas', [5, 10, 20, 1000]);
102
+
103
+ metricLogger.measure('etwas', 2);
104
+ metricLogger.measure('etwas', 3);
105
+ metricLogger.measure('etwas', 4);
106
+ metricLogger.measure('etwas', 6);
107
+ metricLogger.measure('etwas', 11);
108
+ metricLogger.measure('etwas', 12);
109
+ metricLogger.measure('etwas', 816);
110
+
111
+ clock.tick(60 * 1000 + 1);
112
+ expect(Logger.prototype.info).toBeCalledWith('etwas', {
113
+ min: 2,
114
+ max: 816,
115
+ count: 7,
116
+ sum: 854,
117
+ average: 122,
118
+ above_5: 4,
119
+ above_10: 3,
120
+ above_20: 1,
121
+ above_1000: 0,
122
+ is_metric: true
123
+ });
124
+ });
125
+ });
126
+
127
+ describe('start/stop', () => {
128
+ it('should log min, max, count and sum when log was called only once', () => {
129
+ const measurementId = metricLogger.start('utlegeles');
130
+ clock.tick(50);
131
+ metricLogger.stop(measurementId);
132
+
133
+ clock.tick(60 * 1000);
134
+ expect(Logger.prototype.info).toBeCalledWith('utlegeles', {
135
+ min: 50,
136
+ max: 50,
137
+ count: 1,
138
+ sum: 50,
139
+ average: 50,
140
+ is_metric: true
141
+ });
142
+ });
143
+
144
+ it('should aggregate metrics for two measurements and log after a timeout', () => {
145
+ const measurement1Id = metricLogger.start('utlegeles');
146
+ const measurement2Id = metricLogger.start('utlegeles');
147
+ clock.tick(50);
148
+ metricLogger.stop(measurement1Id);
149
+ clock.tick(20);
150
+ metricLogger.stop(measurement2Id);
151
+
152
+ clock.tick(60 * 1000);
153
+ expect(Logger.prototype.info).toBeCalledWith('utlegeles', {
154
+ min: 50,
155
+ max: 70,
156
+ sum: 120,
157
+ count: 2,
158
+ average: 60,
159
+ is_metric: true
160
+ });
161
+ });
162
+
163
+ it('should not count a measurment if it is cancelled with cancel', () => {
164
+ const measurement1Id = metricLogger.start('utlegeles');
165
+ const measurement2Id = metricLogger.start('utlegeles');
166
+ clock.tick(50);
167
+ metricLogger.stop(measurement1Id);
168
+ clock.tick(20);
169
+ metricLogger.cancel(measurement2Id);
170
+ metricLogger.stop(measurement2Id);
171
+
172
+ clock.tick(60 * 1000);
173
+ expect(Logger.prototype.info).toBeCalledWith('utlegeles', expect.objectContaining({
174
+ count: 1
175
+ }));
176
+ });
177
+
178
+ it('should flush results at the closes minute:30', () => {
179
+ const measurementId = metricLogger.start('utlegeles');
180
+ metricLogger.stop(measurementId);
181
+
182
+ clock.tick(29 * 1000 + 999);
183
+ expect(Logger.prototype.info).not.toBeCalled();
184
+ clock.tick(1 * 1000);
185
+ expect(Logger.prototype.info).toBeCalledWith('utlegeles', expect.anything());
186
+ });
187
+
188
+ it('should flush at next minute:30 if we are after 30 in current minute', () => {
189
+ clock.tick(45 * 1000);
190
+ const measurementId = metricLogger.start('utlegeles');
191
+ metricLogger.stop(measurementId);
192
+
193
+ clock.tick(44 * 1000);
194
+ expect(Logger.prototype.info).not.toBeCalled();
195
+ clock.tick(1 * 1000);
196
+ expect(Logger.prototype.info).toBeCalledWith('utlegeles', expect.anything());
197
+ });
198
+
199
+ it('should periodically log measuerments', () => {
200
+ const measurement1 = metricLogger.start('utlegeles');
201
+ const measurement2 = metricLogger.start('utlegeles');
202
+ clock.tick(10 * 1000);
203
+ const measurement3 = metricLogger.start('utlegeles');
204
+ metricLogger.stop(measurement1);
205
+
206
+ clock.tick(21 * 1000);
207
+ expect(Logger.prototype.info).toBeCalledWith('utlegeles', {
208
+ count: 1,
209
+ max: 10000,
210
+ min: 10000,
211
+ sum: 10000,
212
+ average: 10000,
213
+ is_metric: true
214
+ });
215
+
216
+ metricLogger.stop(measurement2);
217
+ metricLogger.stop(measurement3);
218
+ clock.tick(60 * 1000);
219
+ expect(Logger.prototype.info).toBeCalledWith('utlegeles', {
220
+ count: 2,
221
+ max: 31000,
222
+ min: 21000,
223
+ sum: 52000,
224
+ average: 26000,
225
+ is_metric: true
226
+ });
227
+ });
228
+
229
+ it('should log measurements for multiple tags and include extra parameters', () => {
230
+ const measurement1 = metricLogger.start('utlegeles', { a: 21 });
231
+ const measurement2 = metricLogger.start('nyavogas');
232
+ clock.tick(10 * 1000);
233
+ metricLogger.stop(measurement1);
234
+ clock.tick(5 * 1000);
235
+ metricLogger.stop(measurement2);
236
+ clock.tick(15 * 1000);
237
+
238
+ expect(Logger.prototype.info).toBeCalledWith('utlegeles', {
239
+ count: 1,
240
+ max: 10000,
241
+ min: 10000,
242
+ sum: 10000,
243
+ average: 10000,
244
+ a: 21,
245
+ is_metric: true
246
+ });
247
+ expect(Logger.prototype.info).toBeCalledWith('nyavogas', {
248
+ count: 1,
249
+ max: 15000,
250
+ min: 15000,
251
+ sum: 15000,
252
+ average: 15000,
253
+ is_metric: true
254
+ });
255
+ });
256
+
257
+ it('should log warning if there are too many measurements in progress', () => {
258
+ jest.spyOn(Logger.prototype, 'warn');
259
+ const metricLogger = metricLoggerFactory({ inProgressMeasurementWarningLimit: 1 });
260
+
261
+ metricLogger.measure('kacsa');
262
+ metricLogger.start('nyavogas');
263
+ metricLogger.start('nyavogas');
264
+ clock.tick(60 * 1000);
265
+
266
+ expect(Logger.prototype.warn).toBeCalledWith('too-many-in-progress-metric-log-measurements', { count: 2 });
267
+ });
268
+ });
269
+
270
+ describe('simple count one log methods', () => {
271
+ it.each(['trace', 'debug', 'info', 'warn', 'error', 'fatal'])(
272
+ 'should log a only the count when %s method is called',
273
+ method => {
274
+ jest.spyOn(Logger.prototype, method);
275
+
276
+ metricLogger[method]('elfogyasztot-tap', { alma: 2 });
277
+ metricLogger[method]('elfogyasztot-tap', { alma: 2 });
278
+
279
+ clock.tick(60 * 1000 + 11500);
280
+ expect(Logger.prototype[method]).toBeCalledWith('elfogyasztot-tap', {
281
+ count: 2,
282
+ alma: 2,
283
+ is_metric: true
284
+ });
285
+ }
286
+ );
287
+ });
288
+ });