Skip to content

Commit

Permalink
Merge pull request #18 from Velmisov/master
Browse files Browse the repository at this point in the history
Histogram metric addition
  • Loading branch information
Goodluckhf authored Jul 13, 2020
2 parents 49a8b14 + 9934f8a commit ac195b5
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PullTransport from './transport/PullTransport';
import PushTransport from './transport/PushTransport';
import Metric from './metric/Metric';
import GaugeMetric from './metric/GaugeMetric';
import HistogramMetric from './metric/HistogramMetric';

export {
UMetrics,
Expand All @@ -12,4 +13,5 @@ export {
PullTransport,
GaugeMetric,
Metric,
HistogramMetric,
};
60 changes: 60 additions & 0 deletions src/metric/HistogramMetric.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import promClient from 'prom-client';
import Metric from './Metric';

class HistogramMetric extends Metric {
constructor(name, buckets = [], options = {}) {
super(name, options);

this.promMetric = new promClient.Histogram({
name: this.name,
help: options.help || `${this.name}_help`,
labelNames: Object.keys(this._labels),
buckets,
});
}

/**
* @param {string} action
* @param {number} value
* @param {Object.<string, string | number>} [_labels]
* @return {HistogramMetric}
* @private
*/
_proxyCall(action, value, _labels) {
const labels = { ...this._labels, ..._labels };
if (!labels) {
this.promMetric[action](value);
return this;
}

this.promMetric[action](labels, value);
return this;
}

/**
* @param {number} value
* @param {Object.<string, string | number>} [labels]
* @returns {HistogramMetric}
*/
observe(value, labels) {
return this._proxyCall('observe', value, labels);
}

/**
* Start a timer where the value in seconds will observed
* @param labels Object with label keys and values
* @return Function to invoke when timer should be stopped
*/
startTimer(labels) {
return this.promMetric.startTimer(labels);
}

/**
* Reset histogram values
*/
reset() {
this.promMetric.reset();
}
}

export default HistogramMetric;
172 changes: 172 additions & 0 deletions src/metric/HistogramMetric.server.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { expect } from 'chai';
import promClient from 'prom-client';
import HistogramMetric from './HistogramMetric';

/**
* @param {string} metricName
* @return {number}
*/
const getMetricValues = metricName =>
promClient.register.getSingleMetric(metricName).get().values;

/**
* @param {string} metricName
* @param {string} labelName
* @return {string|number}
*/
const getLabelValue = (metricName, labelName) =>
promClient.register.getSingleMetric(metricName).get().values[0].labels[
labelName
];

describe('HistogramMetric', () => {
// Чтобы не ебалось при --watch
afterEach(() => {
promClient.register.clear();
});

describe('observe', () => {
it('Can observe value without labels', () => {
const metricName = 'test';

const metric = new HistogramMetric(metricName, [5, 100, 1000]);

metric.observe(5);
metric.observe(50);
metric.observe(100);
metric.observe(1001);

const metricValues = getMetricValues(metricName);

expect(metricValues[0].labels.le).to.be.equal(5);
expect(metricValues[0].value).to.be.equal(1);
expect(metricValues[0].metricName).to.be.equal(`${metricName}_bucket`);

expect(metricValues[1].labels.le).to.be.equal(100);
expect(metricValues[1].value).to.be.equal(3);

expect(metricValues[2].labels.le).to.be.equal(1000);
expect(metricValues[2].value).to.be.equal(3);

expect(metricValues[3].labels.le).to.be.equal('+Inf');
expect(metricValues[3].value).to.be.equal(4);

expect(metricValues[4].value).to.be.equal(1156);
expect(metricValues[4].metricName).to.be.equal(`${metricName}_sum`);
});

it('Can observe value with labels', () => {
const metricName = 'test';
const labelName = 'testLabel';

const metric = new HistogramMetric(metricName, [100], {
labels: { testLabel: null },
});

metric.observe(50, { [labelName]: 'testLabel' });
metric.observe(150, { [labelName]: 'testLabel' });

const labelValue = getLabelValue(metricName, labelName);
expect(labelValue).to.be.equal(labelName);

const metricValues = getMetricValues(metricName);
expect(metricValues[0].labels.le).to.be.equal(100);
expect(metricValues[0].value).to.be.equal(1);
expect(metricValues[0].metricName).to.be.equal(`${metricName}_bucket`);

expect(metricValues[1].labels.le).to.be.equal('+Inf');
expect(metricValues[1].value).to.be.equal(2);

expect(metricValues[2].value).to.be.equal(200);
expect(metricValues[2].metricName).to.be.equal(`${metricName}_sum`);
});

it('Can observe value without buckets', () => {
const metricName = 'test';

const metric = new HistogramMetric(metricName);

metric.observe(50);
metric.observe(150);

const metricValues = getMetricValues(metricName);

expect(metricValues[0].labels.le).to.be.equal('+Inf');
expect(metricValues[0].value).to.be.equal(2);

expect(metricValues[1].value).to.be.equal(200);
expect(metricValues[1].metricName).to.be.equal(`${metricName}_sum`);
});
});

describe('startTimer', () => {
it('returns function and starts timer', async () => {
const metricName = 'test';

const metric = new HistogramMetric(metricName, [0.005, 0.1, 1]);

let end = metric.startTimer();
await new Promise(resolve => {
setTimeout(() => {
end();
resolve();
}, 2);
});
end = metric.startTimer();
await new Promise(resolve => {
setTimeout(() => {
end();
resolve();
}, 200);
});

const metricValues = getMetricValues(metricName);

expect(metricValues[0].labels.le).to.be.equal(0.005);
expect(metricValues[0].value).to.be.equal(1);
expect(metricValues[0].metricName).to.be.equal(`${metricName}_bucket`);

expect(metricValues[1].labels.le).to.be.equal(0.1);
expect(metricValues[1].value).to.be.equal(1);

expect(metricValues[2].labels.le).to.be.equal(1);
expect(metricValues[2].value).to.be.equal(2);

expect(metricValues[3].labels.le).to.be.equal('+Inf');
expect(metricValues[3].value).to.be.equal(2);
});
});

describe('reset', () => {
it('should reset metrics values', async () => {
const metricName = 'test';

const metric = new HistogramMetric(metricName, [5, 100, 1000]);

metric.observe(5);
metric.observe(101);

metric.reset();

metric.observe(5);

const metricValues = getMetricValues(metricName);

expect(metricValues[0].labels.le).to.be.equal(5);
expect(metricValues[0].value).to.be.equal(1);
expect(metricValues[0].metricName).to.be.equal(`${metricName}_bucket`);

expect(metricValues[1].labels.le).to.be.equal(100);
expect(metricValues[1].value).to.be.equal(1);

expect(metricValues[2].labels.le).to.be.equal(1000);
expect(metricValues[2].value).to.be.equal(1);

expect(metricValues[3].labels.le).to.be.equal('+Inf');
expect(metricValues[3].value).to.be.equal(1);

expect(metricValues[4].value).to.be.equal(5);
expect(metricValues[4].metricName).to.be.equal(`${metricName}_sum`);
});
});
});

0 comments on commit ac195b5

Please sign in to comment.