Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for aliasing the different timer metrics #1

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions backends/graphite.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ var prefixTimer;
var prefixGauge;
var prefixSet;
var globalSuffix;
var aliasCount;
var aliasRate;

// set up namespaces
var legacyNamespace = true;
Expand Down Expand Up @@ -108,9 +110,9 @@ var flush_stats = function graphite_flush(ts, metrics) {
statString += 'stats_counts.' + key + globalSuffix + value + ts_suffix;
}
} else {
statString += namespace.concat('rate').join(".") + globalSuffix + valuePerSecond + ts_suffix;
statString += namespace.concat(aliasRate).join(".") + globalSuffix + valuePerSecond + ts_suffix;
if (flush_counts) {
statString += namespace.concat('count').join(".") + globalSuffix + value + ts_suffix;
statString += namespace.concat(aliasCount).join(".") + globalSuffix + value + ts_suffix;
}
}

Expand Down Expand Up @@ -189,13 +191,17 @@ exports.init = function graphite_init(startup_time, config, events, logger) {
prefixSet = config.graphite.prefixSet;
globalSuffix = config.graphite.globalSuffix;
legacyNamespace = config.graphite.legacyNamespace;
aliasCount = config.graphite.aliasCount;
aliasRate = config.graphite.aliasRate;

// set defaults for prefixes & suffix
globalPrefix = globalPrefix !== undefined ? globalPrefix : "stats";
prefixCounter = prefixCounter !== undefined ? prefixCounter : "counters";
prefixTimer = prefixTimer !== undefined ? prefixTimer : "timers";
prefixGauge = prefixGauge !== undefined ? prefixGauge : "gauges";
prefixSet = prefixSet !== undefined ? prefixSet : "sets";
aliasCount = aliasCount !== undefined ? aliasCount : "count";
aliasRate = aliasRate !== undefined ? aliasRate : "rate";
legacyNamespace = legacyNamespace !== undefined ? legacyNamespace : true;

// In order to unconditionally add this string, it either needs to be
Expand Down
17 changes: 17 additions & 0 deletions exampleConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ Optional Variables:
prefixStats: prefix to use for the statsd statistics data for this running instance of statsd [default: statsd]
applies to both legacy and new namespacing

timerAliases: Individual aliases for the different percentile and timer metrics
meanPrefix: [default 'mean_']
maxPrefix: [default 'upper_']
minPrefix: [default 'lower_']
sumPrefix: [default 'sum_']
stddev: [default 'std']
max: [default 'upper']
min: [default 'lower']
count: [default 'count']
count_ps: [default 'count_ps']
sum: [default 'sum']
mean: [default 'mean']
median: [default 'median']


console:
prettyprint: whether to prettyprint the console backend
output [true or false, default: true]
Expand All @@ -72,6 +87,8 @@ Optional Variables:
globalSuffix: global suffix to use for sending stats to graphite [default: ""]
This is particularly useful for sending per host stats by
settings this value to: require('os').hostname().split('.')[0]
aliasCount: Alias the metric extension for value [default 'count']
aliasRate: Alias the metric for value per second [default 'rate']

repeater: an array of hashes of the for host: and port:
that details other statsd servers to which the received
Expand Down
41 changes: 29 additions & 12 deletions lib/process_metrics.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*jshint node:true, laxcomma:true */

var process_metrics = function (metrics, flushInterval, ts, flushCallback) {
var process_metrics = function (metrics, conf, flushCallback) {
var starttime = Date.now();
var key;
var counter_rates = {};
Expand All @@ -11,6 +11,23 @@ var process_metrics = function (metrics, flushInterval, ts, flushCallback) {
var timer_counters = metrics.timer_counters;
var pctThreshold = metrics.pctThreshold;
var histogram = metrics.histogram;
var flushInterval = conf.flushInterval;
var configAliases = conf.timerAliases || {};

var metricAliases = {
meanPrefix: configAliases.meanPrefix || 'mean_',
maxPrefix: configAliases.maxPrefix || 'upper_',
minPrefix: configAliases.minPrefix || 'lower_',
sumPrefix: configAliases.sumPrefix || 'sum_',
stddev: configAliases.stddev || 'std',
max: configAliases.max || 'upper',
min: configAliases.min || 'lower',
count: configAliases.count || 'count',
count_ps: configAliases.count_ps || 'count_ps',
sum: configAliases.sum || 'sum',
mean: configAliases.mean || 'mean',
median: configAliases.median || 'median'
};

for (key in counters) {
var value = counters[key];
Expand Down Expand Up @@ -60,9 +77,9 @@ var process_metrics = function (metrics, flushInterval, ts, flushCallback) {

var clean_pct = '' + pct;
clean_pct = clean_pct.replace('.', '_').replace('-', 'top');
current_timer_data["mean_" + clean_pct] = mean;
current_timer_data[(pct > 0 ? "upper_" : "lower_") + clean_pct] = thresholdBoundary;
current_timer_data["sum_" + clean_pct] = sum;
current_timer_data[metricAliases.meanPrefix + clean_pct] = mean;
current_timer_data[(pct > 0 ? metricAliases.maxPrefix : metricAliases.minPrefix) + clean_pct] = thresholdBoundary;
current_timer_data[metricAliases.sumPrefix + clean_pct] = sum;

}

Expand All @@ -78,14 +95,14 @@ var process_metrics = function (metrics, flushInterval, ts, flushCallback) {
var median = (count % 2) ? values[mid] : (values[mid-1] + values[mid])/2;

var stddev = Math.sqrt(sumOfDiffs / count);
current_timer_data["std"] = stddev;
current_timer_data["upper"] = max;
current_timer_data["lower"] = min;
current_timer_data["count"] = timer_counters[key];
current_timer_data["count_ps"] = timer_counters[key] / (flushInterval / 1000);
current_timer_data["sum"] = sum;
current_timer_data["mean"] = mean;
current_timer_data["median"] = median;
current_timer_data[metricAliases.stddev] = stddev;
current_timer_data[metricAliases.max] = max;
current_timer_data[metricAliases.min] = min;
current_timer_data[metricAliases.count] = timer_counters[key];
current_timer_data[metricAliases.count_ps] = timer_counters[key] / (flushInterval / 1000);
current_timer_data[metricAliases.sum] = sum;
current_timer_data[metricAliases.mean] = mean;
current_timer_data[metricAliases.median] = median;

// note: values bigger than the upper limit of the last bin are ignored, by design
conf = histogram || [];
Expand Down
4 changes: 2 additions & 2 deletions stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ function flushMetrics() {
}
}

// normally gauges are not reset. so if we don't delete them, continue to persist previous value
// normally gauges are not reset. so if we don't delete them, continue to persist previous value
conf.deleteGauges = conf.deleteGauges || false;
if (conf.deleteGauges) {
for (var gauge_key in metrics.gauges) {
Expand All @@ -127,7 +127,7 @@ function flushMetrics() {
}
});

pm.process_metrics(metrics_hash, flushInterval, time_stamp, function emitFlush(metrics) {
pm.process_metrics(metrics_hash, conf, function emitFlush(metrics) {
backendEvents.emit('flush', time_stamp, metrics);
});

Expand Down
30 changes: 15 additions & 15 deletions test/process_metrics_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,22 @@ module.exports = {
counters_has_stats_count: function(test) {
test.expect(1);
this.metrics.counters['a'] = 2;
pm.process_metrics(this.metrics, 1000, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 1000}, function(){});
test.equal(2, this.metrics.counters['a']);
test.done();
},
counters_has_correct_rate: function(test) {
test.expect(1);
this.metrics.counters['a'] = 2;
pm.process_metrics(this.metrics, 100, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 100}, function(){});
test.equal(20, this.metrics.counter_rates['a']);
test.done();
},
timers_handle_empty: function(test) {
test.expect(1);
this.metrics.timers['a'] = [];
this.metrics.timer_counters['a'] = 0;
pm.process_metrics(this.metrics, 100, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 1000}, function(){});
//potentially a cleaner way to check this
test.equal(undefined, this.metrics.counter_rates['a']);
test.done();
Expand All @@ -49,7 +49,7 @@ module.exports = {
test.expect(8);
this.metrics.timers['a'] = [100];
this.metrics.timer_counters['a'] = 1;
pm.process_metrics(this.metrics, 100, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 100}, function(){});
timer_data = this.metrics.timer_data['a'];
test.equal(0, timer_data.std);
test.equal(100, timer_data.upper);
Expand All @@ -65,7 +65,7 @@ module.exports = {
test.expect(8);
this.metrics.timers['a'] = [100, 200, 300];
this.metrics.timer_counters['a'] = 3;
pm.process_metrics(this.metrics, 100, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 100}, function(){});
timer_data = this.metrics.timer_data['a'];
test.equal(81.64965809277261, timer_data.std);
test.equal(300, timer_data.upper);
Expand All @@ -82,7 +82,7 @@ module.exports = {
this.metrics.timers['a'] = [100];
this.metrics.timer_counters['a'] = 1;
this.metrics.pctThreshold = [90];
pm.process_metrics(this.metrics, 100, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 100}, function(){});
timer_data = this.metrics.timer_data['a'];
test.equal(100, timer_data.mean_90);
test.equal(100, timer_data.upper_90);
Expand All @@ -94,7 +94,7 @@ module.exports = {
this.metrics.timers['a'] = [100];
this.metrics.timer_counters['a'] = 1;
this.metrics.pctThreshold = [90, 80];
pm.process_metrics(this.metrics, 100, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 100}, function(){});
timer_data = this.metrics.timer_data['a'];
test.equal(100, timer_data.mean_90);
test.equal(100, timer_data.upper_90);
Expand All @@ -109,7 +109,7 @@ module.exports = {
this.metrics.timers['a'] = [100, 200, 300];
this.metrics.timer_counters['a'] = 3;
this.metrics.pctThreshold = [90];
pm.process_metrics(this.metrics, 100, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 100}, function(){});
timer_data = this.metrics.timer_data['a'];
test.equal(200, timer_data.mean_90);
test.equal(300, timer_data.upper_90);
Expand All @@ -121,7 +121,7 @@ module.exports = {
this.metrics.timers['a'] = [100, 200, 300];
this.metrics.timer_counters['a'] = 3;
this.metrics.pctThreshold = [90, 80];
pm.process_metrics(this.metrics, 100, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 100}, function(){});
timer_data = this.metrics.timer_data['a'];
test.equal(200, timer_data.mean_90);
test.equal(300, timer_data.upper_90);
Expand All @@ -136,7 +136,7 @@ module.exports = {
this.metrics.timers['a'] = [100, 200, 300];
this.metrics.timer_counters['a'] = 50;
this.metrics.pctThreshold = [90, 80];
pm.process_metrics(this.metrics, 100, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 100}, function(){});
timer_data = this.metrics.timer_data['a'];
test.equal(50, timer_data.count);
test.equal(500, timer_data.count_ps);
Expand All @@ -160,7 +160,7 @@ module.exports = {
{ metric: 'abcd', bins: [ 1, 5, 'inf'] },
{ metric: 'abc', bins: [ 1, 2.21, 'inf'] },
{ metric: 'a', bins: [ 1, 2] } ];
pm.process_metrics(this.metrics, 100, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 100}, function(){});
timer_data = this.metrics.timer_data;
// nothing matches the 'abcd' config, so nothing has bin_5
test.equal(undefined, timer_data['a']['histogram']['bin_5']);
Expand Down Expand Up @@ -190,7 +190,7 @@ module.exports = {
test.expect(3);
this.metrics.timers['a'] = [100];
this.metrics.pctThreshold = [-10];
pm.process_metrics(this.metrics, 100, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 100}, function(){});
timer_data = this.metrics.timer_data['a'];
test.equal(100, timer_data.mean_top10);
test.equal(100, timer_data.lower_top10);
Expand All @@ -201,7 +201,7 @@ module.exports = {
test.expect(3);
this.metrics.timers['a'] = [10, 10, 10, 10, 10, 10, 10, 10, 100, 200];
this.metrics.pctThreshold = [-20];
pm.process_metrics(this.metrics, 100, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 100}, function(){});
timer_data = this.metrics.timer_data['a'];
test.equal(150, timer_data.mean_top20);
test.equal(100, timer_data.lower_top20);
Expand All @@ -210,15 +210,15 @@ module.exports = {
},
statsd_metrics_exist: function(test) {
test.expect(1);
pm.process_metrics(this.metrics, 100, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 100}, function(){});
statsd_metrics = this.metrics.statsd_metrics;
test.notEqual(undefined, statsd_metrics["processing_time"]);
test.done();
},
timers_multiple_times_even: function(test) {
test.expect(1);
this.metrics.timers['a'] = [300, 200, 400, 100];
pm.process_metrics(this.metrics, 100, this.time_stamp, function(){});
pm.process_metrics(this.metrics, {flushInterval: 100}, function(){});
timer_data = this.metrics.timer_data['a'];
test.equal(250, timer_data.median);
test.done();
Expand Down