Skip to content

Commit

Permalink
Support choosing aggregates and percentiles in histograms
Browse files Browse the repository at this point in the history
  • Loading branch information
gquinteros93 authored Sep 7, 2022
1 parent 56aa3e2 commit c95921e
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 20 deletions.
4 changes: 2 additions & 2 deletions lib/aggregators.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ Aggregator.prototype.makeBufferKey = function(key, tags) {
return key + '#' + tags.concat().sort().join('.');
};

Aggregator.prototype.addPoint = function(Type, key, value, tags, host, timestampInMillis) {
Aggregator.prototype.addPoint = function(Type, key, value, tags, host, timestampInMillis, options) {
const bufferKey = this.makeBufferKey(key, tags);
if (!this.buffer.hasOwnProperty(bufferKey)) {
this.buffer[bufferKey] = new Type(key, tags, host);
this.buffer[bufferKey] = new Type(key, tags, host, options);
}

this.buffer[bufferKey].addPoint(value, timestampInMillis);
Expand Down
8 changes: 4 additions & 4 deletions lib/loggers.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ function BufferedMetricsLogger(opts) {
}

// Prepend the global key prefix and set the default host.
BufferedMetricsLogger.prototype.addPoint = function(Type, key, value, tags, timestampInMillis) {
this.aggregator.addPoint(Type, this.prefix + key, value, tags, this.host, timestampInMillis);
BufferedMetricsLogger.prototype.addPoint = function(Type, key, value, tags, timestampInMillis, options) {
this.aggregator.addPoint(Type, this.prefix + key, value, tags, this.host, timestampInMillis, options);
};

BufferedMetricsLogger.prototype.gauge = function(key, value, tags, timestampInMillis) {
Expand All @@ -80,8 +80,8 @@ BufferedMetricsLogger.prototype.increment = function(key, value, tags, timestamp
}
};

BufferedMetricsLogger.prototype.histogram = function(key, value, tags, timestampInMillis) {
this.addPoint(Histogram, key, value, tags, timestampInMillis);
BufferedMetricsLogger.prototype.histogram = function(key, value, tags, timestampInMillis, options = {}) {
this.addPoint(Histogram, key, value, tags, timestampInMillis, options);
};

BufferedMetricsLogger.prototype.flush = function(onSuccess, onError) {
Expand Down
45 changes: 31 additions & 14 deletions lib/metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ const util = require('util');
// --- Metric (base class)
//

const DEFAULT_HISTOGRAM_AGGREGATES = ['max', 'min', 'sum', 'avg', 'count'];
const DEFAULT_HISTOGRAM_PERCENTILES = [0.75, 0.85, 0.95, 0.99];

function Metric(key, tags, host) {
this.key = key;
this.tags = tags || [];
Expand Down Expand Up @@ -110,14 +113,15 @@ Counter.prototype.flush = function() {
// Optionally, specify a list of *tags* to associate with the metric.
//

function Histogram(key, tags, host) {
function Histogram(key, tags, host, options = {}) {
Metric.call(this, key, tags, host);
this.min = Infinity;
this.max = -Infinity;
this.sum = 0;
this.count = 0;
this.samples = [];
this.percentiles = [0.75, 0.85, 0.95, 0.99];
this.aggregates = options.aggregates || DEFAULT_HISTOGRAM_AGGREGATES;
this.percentiles = options.percentiles || DEFAULT_HISTOGRAM_PERCENTILES;
}

util.inherits(Histogram, Metric);
Expand All @@ -136,26 +140,39 @@ Histogram.prototype.addPoint = function(val, timestampInMillis) {
this.samples.push(val);
};

Histogram.prototype.flush = function() {
const points = [
this.serializeMetric(this.min, 'gauge', this.key + '.min'),
this.serializeMetric(this.max, 'gauge', this.key + '.max'),
this.serializeMetric(this.sum, 'gauge', this.key + '.sum'),
this.serializeMetric(this.count, 'count', this.key + '.count'),
Histogram.prototype.flush = function () {
let points = [];
if (this.aggregates.indexOf('min') !== -1) {
points.push(this.serializeMetric(this.min, 'gauge', this.key + '.min'));
}
if (this.aggregates.indexOf('max') !== -1) {
points.push(this.serializeMetric(this.max, 'gauge', this.key + '.max'));
}
if (this.aggregates.indexOf('sum') !== -1) {
points.push(this.serializeMetric(this.sum, 'gauge', this.key + '.sum'));
}
if (this.aggregates.indexOf('count') !== -1) {
points.push(this.serializeMetric(this.count, 'count', this.key + '.count'));
}
if (this.aggregates.indexOf('avg') !== -1) {
points.push(
this.serializeMetric(this.average(), 'gauge', this.key + '.avg')
];

);
}

// Careful, calling samples.sort() will sort alphabetically giving
// the wrong result. We must define our own compare function.
const numericalSortAscending = function(a, b) { return a - b; };
const numericalSortAscending = function (a, b) {
return a - b;
};
this.samples.sort(numericalSortAscending);

const calcPercentile = function(p) {
const calcPercentile = function (p) {
const val = this.samples[Math.round(p * this.samples.length) - 1];
const suffix = '.' + Math.floor(p * 100) + 'percentile';
return this.serializeMetric(val, 'gauge', this.key + suffix);
};

const percentiles = this.percentiles.map(calcPercentile, this);
return points.concat(percentiles);
};
Expand Down
26 changes: 26 additions & 0 deletions test/metrics_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,30 @@ describe('Histogram', function() {
f.should.have.deep.property('[8].metric', 'hist.99percentile');
f.should.have.deep.property('[8].points[0][1]', 99);
});

it('should use custom percentiles and aggregates', function() {
const aggregates = ['avg'];
const percentiles = [0.85];
const h = new metrics.Histogram('hist', [], 'myhost', { aggregates, percentiles });
h.addPoint(1);
var f = h.flush();

f.should.have.deep.property('[0].metric', 'hist.avg');
f.should.have.deep.property('[0].points[0][1]', 1);

f.should.have.deep.property('[1].metric', 'hist.85percentile');
f.should.have.deep.property('[1].points[0][1]', 1);

// Create 100 samples from [1..100] so we can
// verify the calculated percentiles.
for (var i = 2; i <= 100; i++) {
h.addPoint(i);
}
f = h.flush();

f.should.have.deep.property('[1].metric', 'hist.85percentile');
f.should.have.deep.property('[1].points[0][1]', 85);

});

});

0 comments on commit c95921e

Please sign in to comment.