Socket
Socket
Sign inDemoInstall

aggregate-metric-logger

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

aggregate-metric-logger - npm Package Compare versions

Comparing version 1.0.0 to 1.1.0

15

package.json
{
"name": "aggregate-metric-logger",
"version": "1.0.0",
"version": "1.1.0",
"description": "aggregate metrics in memory for logging",
"main": "src/index.js",
"scripts": {
"test": "mocha 'src/*.spec.js'",
"test:watch": "mocha 'src/*.spec.js' --watch"
"test": "jest",
"test:watch": "jest --watch --bail"
},

@@ -28,10 +28,11 @@ "repository": {

"date-fns": "^2.3.0",
"json-stable-stringify": "^1.0.1",
"lodash": "^4.17.15",
"uuid": "^3.3.3"
},
"devDependencies": {
"chai": "^4.2.0",
"mocha": "^6.2.0",
"sinon": "^7.5.0",
"sinon-chai": "^3.3.0"
"@sinonjs/fake-timers": "^6.0.0",
"@types/jest": "^25.1.3",
"jest": "^25.1.0"
}
}

@@ -41,2 +41,23 @@ # aggregate-metric-logger

}
```
```
### Group by extra parameters
If you want to group by additional parameters inside a tag, you can pass down these extra
params in an object to `count` and `start` as the last optional argument:
```js
metricLogger.count('thing-to-count', 14, { customer_id: 12 });
const measurementId = metricLogger.start(
'tag-for-the-measuement',
{ event_type: 'nyul', account: 3 }
);
```
A separete log entry will be created for each seen value combinations seen in the extra params
and these extra params will appended to the log entry.
Be very carefull when using extra params, and only include information you really need.
If you include too many parameters, you could loose the advantage you gain by aggregating your logs
with this package.

@@ -6,2 +6,4 @@ 'use strict';

const { getSeconds, startOfMinute, addSeconds } = require('date-fns');
const { omit, pick } = require('lodash');
const stringify = require('json-stable-stringify');

@@ -12,3 +14,3 @@ module.exports = ({ enabled = true, namespace = 'aggregate-metric-logger' } = {}) => {

let measurements = {};
let metricsByTag = {};
let metrics = {};
let initialized = false;

@@ -18,3 +20,3 @@

logMetrics();
metricsByTag = {};
metrics = {};
setupNextFlush();

@@ -31,5 +33,8 @@ };

const addMeasurement = ({ tag, value }) => {
if (!metricsByTag[tag]) {
metricsByTag[tag] = {
const addMeasurement = ({ tag, value, params }) => {
const key = `${tag}_${stringify(params)}`;
if (!metrics[key]) {
metrics[key] = {
tag,
...params,
min: value,

@@ -42,8 +47,8 @@ max: value,

} else {
const metricsSoFar = metricsByTag[tag];
metricsByTag[tag].min = Math.min(metricsSoFar.min, value);
metricsByTag[tag].max = Math.max(metricsSoFar.max, value);
metricsByTag[tag].sum = metricsSoFar.sum + value;
metricsByTag[tag].count = metricsSoFar.count + 1;
metricsByTag[tag].avarage = metricsByTag[tag].sum / metricsByTag[tag].count
const metricsSoFar = metrics[key];
metrics[key].min = Math.min(metricsSoFar.min, value);
metrics[key].max = Math.max(metricsSoFar.max, value);
metrics[key].sum = metricsSoFar.sum + value;
metrics[key].count = metricsSoFar.count + 1;
metrics[key].avarage = metrics[key].sum / metrics[key].count
}

@@ -53,6 +58,6 @@ };

const logMetrics = () =>
Object.entries(metricsByTag).forEach(([tag, metrics]) => logger.info(tag, metrics));
Object.values(metrics).forEach(metrics => logger.info(metrics.tag, omit(metrics, ['tag'])));
return {
count(tag, value) {
count(tag, value, params) {
if (!enabled) {

@@ -65,5 +70,5 @@ return;

}
addMeasurement({ tag, value });
addMeasurement({ tag, value, params });
},
start(tag) {
start(tag, params) {
if (!enabled) {

@@ -78,3 +83,3 @@ return;

const id = uuid();
measurements[id] = { tag, start: Date.now() };
measurements[id] = { tag, params, start: Date.now() };
return id;

@@ -90,3 +95,6 @@ },

}
addMeasurement({ tag: measurement.tag, value: Date.now() - measurement.start });
addMeasurement({
...pick(measurement, ['tag', 'params']),
value: Date.now() - measurement.start
});
delete measurements[measurementId];

@@ -93,0 +101,0 @@ }

'use strict';
const sinon = require('sinon');
const chai = require('chai');
chai.use(require('sinon-chai'));
const expect = chai.expect;
afterEach(() => sinon.restore());
const metricLoggerFactory = require('./metric-logger');
const Logger = require('@emartech/json-logger').Logger;
const fakeTimers = require('@sinonjs/fake-timers');
jest.spyOn(Logger.prototype, 'info');
let metricLogger;
beforeEach(() => (metricLogger = metricLoggerFactory()));
let clock;
beforeEach(() => {
jest.resetAllMocks();
metricLogger = metricLoggerFactory();
clock = fakeTimers.install();
});
afterEach(() => clock.uninstall());

@@ -18,9 +20,6 @@ describe('metricLogger', () => {

it('should log a single measurement at the next flush period', () => {
const clock = sinon.useFakeTimers();
sinon.stub(Logger.prototype, 'info');
metricLogger.count('elfogyasztot-tap', 4);
clock.tick(60 * 1000 + 1);
expect(Logger.prototype.info).to.have.been.calledWith('elfogyasztot-tap', {
expect(Logger.prototype.info).toBeCalledWith('elfogyasztot-tap', {
min: 4,

@@ -35,5 +34,2 @@ max: 4,

it('should aggregate measurements withing a flush period', () => {
const clock = sinon.useFakeTimers();
sinon.stub(Logger.prototype, 'info');
metricLogger.count('elfogyasztot-tap', 4);

@@ -44,3 +40,3 @@ metricLogger.count('elfogyasztot-tap', 8);

clock.tick(60 * 1000 + 1);
expect(Logger.prototype.info).to.have.been.calledWith('elfogyasztot-tap', {
expect(Logger.prototype.info).toBeCalledWith('elfogyasztot-tap', {
min: 4,

@@ -52,3 +48,3 @@ max: 8,

});
expect(Logger.prototype.info).to.have.been.calledWith('kacsa-cucc', {
expect(Logger.prototype.info).toBeCalledWith('kacsa-cucc', {
min: 18,

@@ -61,2 +57,42 @@ max: 18,

});
it('should log counts for each combination of extra params provedid within a tag', () => {
metricLogger.count('elfogyasztot-tap', 4, { emberke: 'bela' });
metricLogger.count('elfogyasztot-tap', 8, { emberke: 'bela' });
metricLogger.count('elfogyasztot-tap', 18, { emberke: 'jano' });
clock.tick(60 * 1000 + 1);
expect(Logger.prototype.info).toBeCalledWith('elfogyasztot-tap', {
min: 4,
max: 8,
count: 2,
sum: 12,
avarage: 6,
emberke: 'bela'
});
expect(Logger.prototype.info).toBeCalledWith('elfogyasztot-tap', {
min: 18,
max: 18,
count: 1,
sum: 18,
avarage: 18,
emberke: 'jano'
});
});
it('should group logs with same tag and deeply equal params together', () => {
metricLogger.count('elfogyasztot-tap', 4, { a: 1, b: 2 });
metricLogger.count('elfogyasztot-tap', 8, { b: 2, a: 1 });
clock.tick(60 * 1000 + 1);
expect(Logger.prototype.info).toBeCalledWith('elfogyasztot-tap', {
min: 4,
max: 8,
count: 2,
sum: 12,
avarage: 6,
a: 1,
b: 2
});
});
});

@@ -66,5 +102,2 @@

it('should log min, max, count and sum when log was called only once', () => {
const clock = sinon.useFakeTimers();
sinon.stub(Logger.prototype, 'info');
const measurementId = metricLogger.start('utlegeles');

@@ -75,3 +108,3 @@ clock.tick(50);

clock.tick(60 * 1000);
expect(Logger.prototype.info).to.have.been.calledWith('utlegeles', {
expect(Logger.prototype.info).toBeCalledWith('utlegeles', {
min: 50,

@@ -86,5 +119,2 @@ max: 50,

it('should aggregate metrics for two measurements and log after a timeout', () => {
const clock = sinon.useFakeTimers();
sinon.stub(Logger.prototype, 'info');
const measurement1Id = metricLogger.start('utlegeles');

@@ -98,3 +128,3 @@ const measurement2Id = metricLogger.start('utlegeles');

clock.tick(60 * 1000);
expect(Logger.prototype.info).to.have.been.calledWith('utlegeles', {
expect(Logger.prototype.info).toBeCalledWith('utlegeles', {
min: 50,

@@ -109,18 +139,13 @@ max: 70,

it('should flush results at the closes minute:30', () => {
const clock = sinon.useFakeTimers(10 * 1000);
sinon.stub(Logger.prototype, 'info');
const measurementId = metricLogger.start('utlegeles');
metricLogger.stop(measurementId);
clock.tick(19 * 1000 + 999);
expect(Logger.prototype.info).not.to.have.been.called;
clock.tick(29 * 1000 + 999);
expect(Logger.prototype.info).not.toBeCalled();
clock.tick(1 * 1000);
expect(Logger.prototype.info).to.have.been.calledWith('utlegeles');
expect(Logger.prototype.info).toBeCalledWith('utlegeles', expect.anything());
});
it('should flush at next minute:30 if we are after 30 in current minute', () => {
const clock = sinon.useFakeTimers(45 * 1000);
sinon.stub(Logger.prototype, 'info');
clock.tick(45 * 1000);
const measurementId = metricLogger.start('utlegeles');

@@ -130,11 +155,8 @@ metricLogger.stop(measurementId);

clock.tick(44 * 1000);
expect(Logger.prototype.info).not.to.have.been.called;
expect(Logger.prototype.info).not.toBeCalled();
clock.tick(1 * 1000);
expect(Logger.prototype.info).to.have.been.calledWith('utlegeles');
expect(Logger.prototype.info).toBeCalledWith('utlegeles', expect.anything());
});
it('should periodically log measuerments', () => {
const clock = sinon.useFakeTimers(0);
sinon.stub(Logger.prototype, 'info');
const measurement1 = metricLogger.start('utlegeles');

@@ -147,3 +169,3 @@ const measurement2 = metricLogger.start('utlegeles');

clock.tick(21 * 1000);
expect(Logger.prototype.info).to.have.been.calledWith('utlegeles', {
expect(Logger.prototype.info).toBeCalledWith('utlegeles', {
count: 1,

@@ -159,3 +181,3 @@ max: 10000,

clock.tick(60 * 1000);
expect(Logger.prototype.info).to.have.been.calledWith('utlegeles', {
expect(Logger.prototype.info).toBeCalledWith('utlegeles', {
count: 2,

@@ -169,7 +191,4 @@ max: 31000,

it('should log measurements for multiple tags', () => {
const clock = sinon.useFakeTimers(0);
sinon.stub(Logger.prototype, 'info');
const measurement1 = metricLogger.start('utlegeles');
it('should log measurements for multiple tags and include extra parameters', () => {
const measurement1 = metricLogger.start('utlegeles', { a: 21 });
const measurement2 = metricLogger.start('nyavogas');

@@ -182,3 +201,3 @@ clock.tick(10 * 1000);

expect(Logger.prototype.info).to.have.been.calledWith('utlegeles', {
expect(Logger.prototype.info).toBeCalledWith('utlegeles', {
count: 1,

@@ -188,5 +207,6 @@ max: 10000,

sum: 10000,
avarage: 10000
avarage: 10000,
a: 21
});
expect(Logger.prototype.info).to.have.been.calledWith('nyavogas', {
expect(Logger.prototype.info).toBeCalledWith('nyavogas', {
count: 1,

@@ -193,0 +213,0 @@ max: 15000,

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc