Skip to content

Commit

Permalink
Gauge and CachedGauge support (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
rafalsiwiec authored and tolbertam committed Oct 8, 2018
1 parent 71f3b93 commit d3a7a8b
Show file tree
Hide file tree
Showing 15 changed files with 164 additions and 15 deletions.
18 changes: 17 additions & 1 deletion index.d.ts
Expand Up @@ -3,7 +3,7 @@
import events = require("events");

declare namespace metrics {
type Metric = Meter | Timer | Counter | Histogram;
type Metric = Meter | Timer | Counter | Histogram | Gauge;

type MeterPrintObj = {
type: "meter",
Expand Down Expand Up @@ -82,6 +82,22 @@ declare namespace metrics {
});
}

class Gauge {
type: "gauge";

constructor(valueFn: () => any);
value: () => any

printObj: () => ({
type: "gauge";
value: any;
})
}

class CachedGauge extends Gauge {
constructor(valueFn: () => any, expirationInMs: number);
}

type HistogramPrintObj = {
type: "histogram",

Expand Down
2 changes: 2 additions & 0 deletions index.js
Expand Up @@ -6,6 +6,8 @@ exports.Histogram = Metrics.Histogram;
exports.Meter = Metrics.Meter;
exports.Counter = Metrics.Counter;
exports.Timer = Metrics.Timer;
exports.Gauge = Metrics.Gauge;
exports.CachedGauge = Metrics.CachedGauge;

exports.Server = Reporting.Server;
exports.Report = Reporting.Report;
Expand Down
21 changes: 21 additions & 0 deletions metrics/cached_gauge.js
@@ -0,0 +1,21 @@
var Gauge = require('./gauge'),
util = require('util');

function now() {
return new Date().getTime();
}

function CachedGauge(valueFn, expirationInMs) {
var value, lastRefresh;
Gauge.call(this, function() {
if (!lastRefresh || now() - lastRefresh > expirationInMs) {
value = valueFn();
lastRefresh = now();
}
return value;
});
}

util.inherits(CachedGauge, Gauge);

module.exports = CachedGauge
8 changes: 8 additions & 0 deletions metrics/gauge.js
@@ -0,0 +1,8 @@
var Gauge = module.exports = function Gauge(valueFn) {
this.value = valueFn;
this.type = 'gauge';
}

Gauge.prototype.printObj = function() {
return {type: 'gauge', value: this.value()};
}
2 changes: 2 additions & 0 deletions metrics/index.js
Expand Up @@ -3,4 +3,6 @@ exports.Counter = require('./counter');
exports.Histogram = require('./histogram');
exports.Meter = require('./meter');
exports.Timer = require('./timer');
exports.Gauge = require('./gauge');
exports.CachedGauge = require('./cached_gauge');

13 changes: 13 additions & 0 deletions reporting/console-reporter.js
Expand Up @@ -54,6 +54,14 @@ ConsoleReporter.prototype.report = function() {
});
console.log();
}

if(metrics.gauges.length != 0) {
printWithBanner('Gauges');
metrics.gauges.forEach(function (gauge) {
printGauge(gauge);
});
console.log();
}
};

function printWithBanner(name) {
Expand Down Expand Up @@ -120,5 +128,10 @@ function printHistogram(histogram) {
console.log(' 99.9%% <= %s%s', ff(percentiles[.999]), durationUnit);
}

function printGauge(gauge) {
console.log(gauge.name);
console.log(' value = %s', JSON.stringify(gauge.value()));
}

module.exports = ConsoleReporter;

11 changes: 10 additions & 1 deletion reporting/csv-reporter.js
Expand Up @@ -44,7 +44,11 @@ CsvReporter.prototype.report = function() {
if(histogram.min != null) {
self.reportHistogram.bind(self)(histogram, timestamp);
}
})
});

metrics.gauges.forEach(function(gauge) {
self.reportGauge.bind(self)(gauge, timestamp);
});
};

CsvReporter.prototype.write = function(timestamp, name, header, line, values) {
Expand Down Expand Up @@ -135,4 +139,9 @@ CsvReporter.prototype.reportHistogram = function(histogram, timestamp) {
]);
};

CsvReporter.prototype.reportGauge = function(gauge, timestamp) {
var write = this.write.bind(this);
write(timestamp, gauge.name, 'value', '%s', [JSON.stringify(gauge.value())]);
}

module.exports = CsvReporter;
14 changes: 13 additions & 1 deletion reporting/graphite-reporter.js
Expand Up @@ -89,6 +89,12 @@ GraphiteReporter.prototype.report = function() {
}
})
}

if(metrics.gauges.length != 0) {
metrics.counters.forEach(function (gauge) {
self.reportGauge.bind(self)(gauge, timestamp);
})
}
};

GraphiteReporter.prototype.send = function(name, value, timestamp) {
Expand Down Expand Up @@ -155,4 +161,10 @@ GraphiteReporter.prototype.reportHistogram = function(histogram, timestamp) {
send(util.format('%s.%s', histogram.name, 'p999'), percentiles[.999], timestamp);
};

module.exports = GraphiteReporter;
GraphiteReporter.prototype.reportGauge = function(gauge, timestamp) {
var send = this.send.bind(this);

send(gauge.name, JSON.stringify(gauge.count), timestamp);
};

module.exports = GraphiteReporter;
15 changes: 9 additions & 6 deletions reporting/scheduled-reporter.js
Expand Up @@ -3,6 +3,7 @@ var Counter = require('../metrics').Counter,
Histogram = require('../metrics').Histogram,
Meter = require('../metrics').Meter,
Timer = require('../metrics').Timer,
Gauge = require('../metrics').Gauge,
util = require('util'),
EventEmitter = require('events').EventEmitter;

Expand Down Expand Up @@ -54,6 +55,7 @@ ScheduledReporter.prototype.getMetrics = function() {
var timers = [];
var counters = [];
var histograms = [];
var gauges = [];

var trackedMetrics = this.registry.trackedMetrics;
// Flatten metric name to be namespace.name is has a namespace and separate out metrics
Expand All @@ -66,20 +68,21 @@ ScheduledReporter.prototype.getMetrics = function() {
} else {
metric.name = name;
}
var metricType = Object.getPrototypeOf(metric);
if(metricType === Meter.prototype) {
if(metric instanceof Meter) {
meters.push(metric);
} else if(metricType == Timer.prototype) {
} else if(metric instanceof Timer) {
timers.push(metric);
} else if(metricType == Counter.prototype) {
} else if(metric instanceof Counter) {
counters.push(metric);
} else if(metricType == Histogram.prototype) {
} else if(metric instanceof Histogram) {
histograms.push(metric);
} else if(metric instanceof Gauge) {
gauges.push(metric);
}
}
}

return { meters: meters, timers: timers, counters: counters, histograms: histograms };
return { meters: meters, timers: timers, counters: counters, histograms: histograms, gauges: gauges };
};

module.exports = ScheduledReporter;
33 changes: 33 additions & 0 deletions test/unit/cached_gauge.js
@@ -0,0 +1,33 @@
var expect = require('chai').expect
, describe = require('mocha').describe
, it = require('mocha').it
, CachedGauge = require('../../metrics/cached_gauge');

describe('CachedGauge', function() {
function seqFn() {
var i = 0;
return function() {
return i++;
}
}

it('should call function when asked for a value for the first time.', function() {
var gauge = new CachedGauge(seqFn(), 100);
expect(gauge.printObj()).to.have.property('value', 0);
});

it('should not call function before expiration timeout.', function() {
var gauge = new CachedGauge(seqFn(), 1000);
expect(gauge.printObj()).to.have.property('value', 0);
expect(gauge.printObj()).to.have.property('value', 0);
});

it('should call function after expiration timeout.', function(done) {
var gauge = new CachedGauge(seqFn(), 50);
expect(gauge.printObj()).to.have.property('value', 0);
setTimeout(function() {
expect(gauge.printObj()).to.have.property('value', 1);
done();
}, 100);
});
});
8 changes: 6 additions & 2 deletions test/unit/console_reporter.js
Expand Up @@ -29,7 +29,7 @@ describe('ConsoleReporter', function () {

// validate line by line. This may be overkill but will detect when
// unanticipated changes affect reported output.
expect(data.length).to.equal(44);
expect(data.length).to.equal(48);
expect(data[0]).to.equal('Counters -----------------------------------------------------------------------');
expect(data[1]).to.equal('basicCount');
expect(data[2]).to.equal(' count = 5');
Expand Down Expand Up @@ -75,5 +75,9 @@ describe('ConsoleReporter', function () {
expect(data[41]).to.equal(' 99% <= 199.98');
expect(data[42]).to.equal(' 99.9% <= 200.00');
expect(data[43]).to.equal('');
expect(data[44]).to.equal('Gauges -------------------------------------------------------------------------');
expect(data[45]).to.equal('myapp.Gauge');
expect(data[46]).to.equal(' value = 0.8');
expect(data[47]).to.equal('');
});
});
});
10 changes: 8 additions & 2 deletions test/unit/csv_reporter.js
Expand Up @@ -36,7 +36,8 @@ describe('CsvReporter', function () {
var meterFile = path.join(tmpdir, 'myapp.Meter.csv');
var timerFile = path.join(tmpdir, 'myapp.Timer.csv');
var histFile = path.join(tmpdir, 'myapp.Histogram.csv');
var files = [counterFile, meterFile, timerFile, histFile];
var gaugeFile = path.join(tmpdir, 'myapp.Gauge.csv');
var files = [counterFile, meterFile, timerFile, histFile, gaugeFile];

setTimeout(function() {
debug("Reading files %s. Each file should have 1 header and 3 recordings.", files);
Expand Down Expand Up @@ -65,6 +66,11 @@ describe('CsvReporter', function () {
data.slice(1, 4).forEach(function (line) {
expect(line).to.match(/.*,100,200,101,2,58.02298395176403,101,151.5,191.89999999999998,197.96,199.98,200/);
});
} else if (f === gaugeFile) {
expect(data[0]).to.equal('t,value');
data.slice(1, 4).forEach(function (line) {
expect(line).to.match(/.*,0.8/);
});
} else {
expect(data[0]).to.equal('t,count,max,mean,min,stddev,p50,p75,p95,p98,p99,p999,mean_rate,m1_rate,m5_rate,m15_rate,rate_unit,duration_unit');
data.slice(1, 4).forEach(function (line) {
Expand All @@ -83,4 +89,4 @@ describe('CsvReporter', function () {
done();
}, 3500);
});
});
});
14 changes: 14 additions & 0 deletions test/unit/gauge.js
@@ -0,0 +1,14 @@
var expect = require('chai').expect
, describe = require('mocha').describe
, it = require('mocha').it
, Gauge = require('../../metrics/gauge');

describe('Gauge', function() {
it('should call function every time value is requested.', function() {
var i = 0;
var gauge = new Gauge(function() { return i++; });

expect(gauge.printObj()).to.have.property('value', 0);
expect(gauge.printObj()).to.have.property('value', 1);
});
});
5 changes: 3 additions & 2 deletions test/unit/graphite_reporter.js
Expand Up @@ -67,7 +67,8 @@ describe('GraphiteReporter', function () {
// Meter should have 5 values.
// Timer should have 15 values.
// Histogram should have 11 values.
expect(tsData.length).to.equal(32);
// Gauge should have 1 value.
expect(tsData.length).to.equal(33);
// Metric names should start with host name.
tsData.forEach(function (metric) {
expect(metric[0]).to.startsWith('host1.');
Expand Down Expand Up @@ -98,4 +99,4 @@ describe('GraphiteReporter', function () {
reporter.start(1000);
});
});
});
});
5 changes: 5 additions & 0 deletions test/unit/helper.js
Expand Up @@ -4,6 +4,7 @@ var metrics = require('../../'),
Timer = metrics.Timer,
Meter = metrics.Meter,
Histogram = metrics.Histogram,
CachedGauge = metrics.CachedGauge,
util = require('util');

function getSampleReport() {
Expand All @@ -19,11 +20,15 @@ function getSampleReport() {
for (var i = 1; i <= 100; i++) {
hist.update(i*2);
}
var gauge = new CachedGauge(function () {
return 0.8
}, 10000);
var report = new Report();
report.addMetric("basicCount", counter);
report.addMetric("myapp.Meter", meter);
report.addMetric("myapp.Timer", timer);
report.addMetric("myapp.Histogram", hist);
report.addMetric("myapp.Gauge", gauge);
return report;
}

Expand Down

0 comments on commit d3a7a8b

Please sign in to comment.