Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Configurable metric namespace #137

Merged
merged 15 commits into from

8 participants

@mrtazz
Owner

A first stab at a clean implementation of key namespacing based on ideas brought up in other pull requests. This whole pull request handles namespaces in the graphite backend only. Other backends can handle their respective namespacing differently.

There is now an option available to change the global stats prefix to something else, as well as not having one by setting it to the empty string (""). The same goes for counters, timers and gauges which are now located under their type identifiers per default.

Dieterbe and others added some commits
@Dieterbe Dieterbe make prefixes for all metrics configurable a188a69
@mrtazz mrtazz Merge branch 'configurable-prefix' of https://github.com/Dieterbe/statsd
 into configurable-metric-namespace

Conflicts:
	backends/graphite.js
	exampleConfig.js
3903ddd
@mrtazz mrtazz modify key namespacing to be easier to configure
This makes some changes to what the default key namespaces are and how they are
configured. Basically it puts the config under the graphite key and doesn't
need the user to take care of where dots are set.
545d780
@travisbot

This pull request fails (merged 545d780 into 5a36ca0).

@travisbot

This pull request passes (merged 7247c70 into 5a36ca0).

@otac0n

One thing: If we are going to take a backwards incompatible change anyways, could we organize counters in the same way we organize timers?

That is, a timer name like foo.timetaken becomes stats.timers.foo.timetaken.{min|max|count|mean|mean_90|...}, so can we do the same for counters? Namely, foo.ordercreated should (in my opinion) become stats.counters.foo.ordercreated.{rate|count}.

@mrtazz
Owner
@otac0n

This way of coalescing will prevent someone from using an empty prefix.

These should be config.graphite.prefixFoo !== undefined ? "foos" : config.graphite.prefixFoo.

Owner

I'm not sure I understand that correctly. But your line does the complete opposite. If it's not undefined (has a value), it's set to "foos" and otherwise it's set to the key ( which is undefined then)?

Ah, yeah, I have the Boolean logic backwards, but the point is that a simple || operator will not have the desired effect here.

@mrtazz
Owner

@otac0n The changes are not backwards incompatible as a whole. Only the numStats and stats_counts keys are located slightly different to respect the cleaned up namespacing. But you can still configure it to put the keys in the old place.

And an empty prefix is possible by setting it to "". This way it will not get pushed into the namespace.

@otac0n

@mrtazz I'm saying that the code that is currently checked in will treat "" as the same as "unset" and will coalesce it to "stats" or whatever.

That is, "" || foo will always return foo. Check the lines of code I annotated.

@timbunce

Just a note to say I'm really looking forward to this pull request, or something very like it, landing soon.

@mheffner

In addition to this it would be nice to have a version of the "globalPrefix" variable at the top-level that was respected across backends. I think a single scoping namespace (e.g, "stg", "prod", "test") is the most common use case for name spacing and it would be nice to not have to specify that for each backend.

The default could be "" and the full namespace would be concatenated with the backend's globalPrefix: globalPrefix + backend[globalPrefix] + metric_name.

This is probably best to do in a separate issue/PR, but just thought I'd see what people thought.

@mrtazz
Owner
@otac0n

Sounds good, but I think they should be concatenated in the opposite order.
Reason 1: If you are programmatically accessing the data from multiple backends, it would be simplest to use a "ends with" check to find a particular stat. Like stats.Where(s => s.EndsWith("prod.logins.failures.count")).

Reason 2: The backend-specific prefix is intended to help separate StatsD stats from other sources, and should come first.

Reason 3: It would be silly (in graphite) to have 3 folders (prod, test, debug) that contained exactly one folder each (stats).

So, for those reasons, I feel that the key should look something like stats.prod.your.key.here

@mheffner

@otac0n Agreed, that sounds like a better order.

@type11error

Wondering if this is going to be pulled soon (or something like it). Waiting for this feature before we can implement statsd. Thanks!

@timbunce

I just want to echo @type11error and say that we're also waiting for something like this to land soon.
And while i'm here, I agree with @otac0n (stats.prod.your.key.here).

@mrtazz
Owner

Sorry for the lag. I want to have this in master at the beginning of next week at the latest. Want to make it a bit more backwards compatible before merging.

@mrtazz mrtazz Merge branch 'master' into configurable-metric-namespace
Conflicts:
	backends/graphite.js
	exampleConfig.js
d9fa4c0
@mrtazz mrtazz was assigned
mrtazz added some commits
@mrtazz mrtazz add a flag for namespacing backwards compatibility
This introduces the `graphite.legacyNamespace` boolean flag which can be used
to maintain backwards compatibility to how stats are sent to graphite.
b35ab92
@mrtazz mrtazz update tests to reflect non-legacy namespace 55126a2
@mrtazz mrtazz add tests for graphite legacy namespace 3d15401
@mrtazz mrtazz add set type to configurable namespace 5f50a06
@mrtazz
Owner

The above commits add the possibility to continue to use the legacy namespace. The legacy namespace will probably stay default for now and the new one will have to be explicitly enabled. I want to give this some more testing time and also try to add a good implementation of the global prefix across backends.

@alfredodeza alfredodeza referenced this pull request in WoLpH/python-statsd
Closed

Naming on metrics is not consistent #17

@timbunce

Hello @mrtazz, any news on this? Anything we can do to help?

@otac0n

We have pulled this branch into our production environment. It is working great for us, so far. I hope to see this land in master soon! Thanks, @mrtazz.

@mrtazz
Owner

@otac0n awesome. Are you running it with custom namespaces?

@obazoud

Awesome feature!
and I would love to see this feature merged in master.

@Dieterbe
  • another readme typo: legacyNamspace
  • would it be possible to include a small script that moves whisper files around from the legacy locations to the new defaults? i bet that would be useful for pretty much every statsd user.
@mrtazz
Owner

Good catch. Yeah an upgrade script from the basic old to new namespace would be helpful I think.

@mrtazz mrtazz merged commit dbd0597 into from
@lambdafu lambdafu referenced this pull request in trbs/bucky
Closed

Implement configurable metric namespace. #20

@lambdafu lambdafu referenced this pull request in trbs/bucky
Merged

Implement configurable metric namespace. #21

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 18, 2012
  1. @Dieterbe
Commits on Aug 7, 2012
  1. @mrtazz

    Merge branch 'configurable-prefix' of https://github.com/Dieterbe/statsd

    mrtazz authored
     into configurable-metric-namespace
    
    Conflicts:
    	backends/graphite.js
    	exampleConfig.js
Commits on Aug 8, 2012
  1. @mrtazz

    modify key namespacing to be easier to configure

    mrtazz authored
    This makes some changes to what the default key namespaces are and how they are
    configured. Basically it puts the config under the graphite key and doesn't
    need the user to take care of where dots are set.
  2. @mrtazz

    fix the tests

    mrtazz authored
Commits on Sep 25, 2012
  1. @mrtazz

    Merge branch 'master' into configurable-metric-namespace

    mrtazz authored
    Conflicts:
    	backends/graphite.js
    	exampleConfig.js
  2. @mrtazz

    add a flag for namespacing backwards compatibility

    mrtazz authored
    This introduces the `graphite.legacyNamespace` boolean flag which can be used
    to maintain backwards compatibility to how stats are sent to graphite.
  3. @mrtazz
  4. @mrtazz
  5. @mrtazz
Commits on Sep 29, 2012
  1. @mrtazz
Commits on Nov 25, 2012
  1. @mrtazz

    Merge branch 'master' into configurable-metric-namespace

    mrtazz authored
    Conflicts:
    	backends/graphite.js
  2. @mrtazz
  3. @mrtazz
  4. @mrtazz

    fix typos in README

    mrtazz authored
  5. @mrtazz
This page is out of date. Refresh to see the latest.
View
39 README.md
@@ -90,8 +90,8 @@ Supported Backends
StatsD supports pluggable backend modules that can publish
statistics from the local StatsD daemon to a backend service or data
-store. Backend services can retain statistics in a time series data store,
-visualize statistics in graphs or tables, or generate alerts based on
+store. Backend services can retain statistics in a time series data store,
+visualize statistics in graphs or tables, or generate alerts based on
defined thresholds. A backend can also correlate statistics sent from StatsD
daemons running across multiple hosts in an infrastructure.
@@ -104,12 +104,12 @@ StatsD includes the following built-in backends:
* Repeater (`repeater`): Utilizes the `packet` emit API to
forward raw packets retrieved by StatsD to multiple backend StatsD instances.
-A robust set of [other backends](https://github.com/etsy/statsd/wiki/Backends)
-are also available as plugins to allow easy reporting into databases, queues
+A robust set of [other backends](https://github.com/etsy/statsd/wiki/Backends)
+are also available as plugins to allow easy reporting into databases, queues
and third-party services.
By default, the `graphite` backend will be loaded automatically. Multiple
-backends can be run at once. To select which backends are loaded, set
+backends can be run at once. To select which backends are loaded, set
the `backends` configuration variable to the list of backend modules to load.
Backends are just npm modules which implement the interface described in
@@ -311,6 +311,35 @@ metrics: {
the raw received message string and the `rinfo` paramter contains remote
address information from the UDP socket.
+
+Metric namespacing
+-------------------
+The metric namespacing in the Graphite backend is configurable with regard to
+the prefixes. Per default all stats are put under `stats` in Graphite, which
+makes it easier to consolidate them all under one schema. However it is
+possible to change these namespaces in the backend configuration options.
+The available configuration options (living under the `graphite` key) are:
+
+```
+legacyNamespace: use the legacy namespace [default: true]
+globalPrefix: global prefix to use for sending stats to graphite [default: "stats"]
+prefixCounter: graphite prefix for counter metrics [default: "counters"]
+prefixTimer: graphite prefix for timer metrics [default: "timers"]
+prefixGauge: graphite prefix for gauge metrics [default: "gauges"]
+prefixSet: graphite prefix for set metrics [default: "sets"]
+```
+
+If you decide not to use the legacy namespacing, besides the obvious changes
+in the prefixing, there will also be a breaking change in the way counters are
+submitted. So far counters didn't live under any namespace and were also a bit
+confusing due to the way they record rate and absolute counts. In the legacy
+setting rates were recorded under `stats.counter_name` directly, whereas the
+absolute count could be found under `stats_count.counter_name`. With disabling
+the legacy namespacing those values can be found (with default prefixing)
+under `stats.counters.counter_name.rate` and
+`stats.counters.counter_name.count` now.
+
+
Inspiration
-----------
View
109 backends/graphite.js
@@ -20,6 +20,22 @@ var flushInterval;
var graphiteHost;
var graphitePort;
+// prefix configuration
+var globalPrefix;
+var prefixPersecond;
+var prefixCounter;
+var prefixTimer;
+var prefixGauge;
+var prefixSet;
+
+// set up namespaces
+var legacyNamespace = true;
+var globalNamespace = [];
+var counterNamespace = [];
+var timerNamespace = [];
+var gaugesNamespace = [];
+var setsNamespace = [];
+
var graphiteStats = {};
var post_stats = function graphite_post_stats(statString) {
@@ -35,8 +51,9 @@ var post_stats = function graphite_post_stats(statString) {
});
graphite.on('connect', function() {
var ts = Math.round(new Date().getTime() / 1000);
- statString += 'stats.statsd.graphiteStats.last_exception ' + last_exception + ' ' + ts + "\n";
- statString += 'stats.statsd.graphiteStats.last_flush ' + last_flush + ' ' + ts + "\n";
+ var namespace = globalNamespace.concat('statsd');
+ statString += namespace.join(".") + '.graphiteStats.last_exception ' + last_exception + ' ' + ts + "\n";
+ statString += namespace.join(".") + '.graphiteStats.last_flush ' + last_flush + ' ' + ts + "\n";
this.write(statString);
this.end();
graphiteStats.last_flush = Math.round(new Date().getTime() / 1000);
@@ -65,8 +82,17 @@ var flush_stats = function graphite_flush(ts, metrics) {
var statsd_metrics = metrics.statsd_metrics;
for (key in counters) {
- statString += 'stats.' + key + ' ' + counter_rates[key] + ' ' + ts + "\n";
- statString += 'stats_counts.' + key + ' ' + counters[key] + ' ' + ts + "\n";
+ var namespace = counterNamespace.concat(key);
+ var value = counters[key];
+ var valuePerSecond = value / (flushInterval / 1000); // calculate "per second" rate
+
+ if (legacyNamespace === true) {
+ statString += namespace.join(".") + ' ' + valuePerSecond + ' ' + ts + "\n";
+ statString += 'stats_counts.' + key + ' ' + value + ' ' + ts + "\n";
+ } else {
+ statString += namespace.concat('rate').join(".") + ' ' + valuePerSecond + ' ' + ts + "\n";
+ statString += namespace.concat('count').join(".") + ' ' + value + ' ' + ts + "\n";
+ }
numStats += 1;
}
@@ -74,7 +100,9 @@ var flush_stats = function graphite_flush(ts, metrics) {
for (key in timer_data) {
if (Object.keys(timer_data).length > 0) {
for (timer_data_key in timer_data[key]) {
- statString += 'stats.timers.' + key + '.' + timer_data_key + ' ' + timer_data[key][timer_data_key] + ' ' + ts + "\n";
+ var namespace = timerNamespace.concat(key);
+ var the_key = namespace.join(".");
+ statString += the_key + '.' + timer_data_key + ' ' + timer_data[key][timer_data_key] + ' ' + ts + "\n";
}
numStats += 1;
@@ -82,23 +110,33 @@ var flush_stats = function graphite_flush(ts, metrics) {
}
for (key in gauges) {
- statString += 'stats.gauges.' + key + ' ' + gauges[key] + ' ' + ts + "\n";
-
+ var namespace = gaugesNamespace.concat(key);
+ statString += namespace.join(".") + ' ' + gauges[key] + ' ' + ts + "\n";
numStats += 1;
}
for (key in sets) {
- statString += 'stats.sets.' + key + '.count ' + sets[key].values().length + ' ' + ts + "\n";
-
+ var namespace = setsNamespace.concat(key);
+ statString += namespace.join(".") + '.count ' + sets[key].values().length + ' ' + ts + "\n";
numStats += 1;
}
- for (key in statsd_metrics) {
- statString += 'stats.statsd.' + key + ' ' + statsd_metrics[key] + ' ' + ts + "\n";
+ var namespace = globalNamespace.concat('statsd');
+ if (legacyNamespace === true) {
+ statString += 'statsd.numStats ' + numStats + ' ' + ts + "\n";
+ statString += 'stats.statsd.graphiteStats.calculationtime ' + (Date.now() - starttime) + ' ' + ts + "\n";
+ for (key in statsd_metrics) {
+ statString += 'stats.statsd.' + key + ' ' + statsd_metrics[key] + ' ' + ts + "\n";
+ }
+ } else {
+ statString += namespace.join(".") + '.numStats ' + numStats + ' ' + ts + "\n";
+ statString += namespace.join(".") + '.graphiteStats.calculationtime ' + (Date.now() - starttime) + ' ' + ts + "\n";
+ for (key in statsd_metrics) {
+ var the_key = namespace.concat(key);
+ statString += the_key.join(".") + ' ' + statsd_metrics[key] + ' ' + ts + "\n";
+ }
}
- statString += 'statsd.numStats ' + numStats + ' ' + ts + "\n";
- statString += 'stats.statsd.graphiteStats.calculationtime ' + (Date.now() - starttime) + ' ' + ts + "\n";
post_stats(statString);
};
@@ -112,6 +150,51 @@ exports.init = function graphite_init(startup_time, config, events) {
debug = config.debug;
graphiteHost = config.graphiteHost;
graphitePort = config.graphitePort;
+ config.graphite = config.graphite || {};
+ globalPrefix = config.graphite.globalPrefix;
+ prefixCounter = config.graphite.prefixCounter;
+ prefixTimer = config.graphite.prefixTimer;
+ prefixGauge = config.graphite.prefixGauge;
+ prefixSet = config.graphite.prefixSet;
+ legacyNamespace = config.graphite.legacyNamespace;
+
+ // set defaults for prefixes
+ 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";
+ legacyNamespace = legacyNamespace !== undefined ? legacyNamespace : true;
+
+
+ if (legacyNamespace === false) {
+ if (globalPrefix !== "") {
+ globalNamespace.push(globalPrefix);
+ counterNamespace.push(globalPrefix);
+ timerNamespace.push(globalPrefix);
+ gaugesNamespace.push(globalPrefix);
+ setsNamespace.push(globalPrefix);
+ }
+
+ if (prefixCounter !== "") {
+ counterNamespace.push(prefixCounter);
+ }
+ if (prefixTimer !== "") {
+ timerNamespace.push(prefixTimer);
+ }
+ if (prefixGauge !== "") {
+ gaugesNamespace.push(prefixGauge);
+ }
+ if (prefixSet !== "") {
+ setsNamespace.push(prefixSet);
+ }
+ } else {
+ globalNamespace = ['stats'];
+ counterNamespace = ['stats'];
+ timerNamespace = ['stats', 'timers'];
+ gaugesNamespace = ['stats', 'gauges'];
+ setsNamespace = ['stats', 'sets'];
+ }
graphiteStats.last_flush = startup_time;
graphiteStats.last_exception = startup_time;
View
8 exampleConfig.js
@@ -44,6 +44,14 @@ Optional Variables:
application: name of the application for syslog [string, default: statsd]
level: log level for [node-]syslog [string, default: LOG_INFO]
+ graphite:
+ legacyNamespace: use the legacy namespace [default: true]
+ globalPrefix: global prefix to use for sending stats to graphite [default: "stats"]
+ prefixCounter: graphite prefix for counter metrics [default: "counters"]
+ prefixTimer: graphite prefix for timer metrics [default: "timers"]
+ prefixGauge: graphite prefix for gauge metrics [default: "gauges"]
+ prefixSet: graphite prefix for set metrics [default: "sets"]
+
repeater: an array of hashes of the for host: and port:
that details other statsd servers to which the received
packets should be "repeated" (duplicated to).
View
229 test/graphite_legacy_tests.js
@@ -0,0 +1,229 @@
+var fs = require('fs'),
+ net = require('net'),
+ temp = require('temp'),
+ spawn = require('child_process').spawn,
+ util = require('util'),
+ urlparse = require('url').parse,
+ _ = require('underscore'),
+ dgram = require('dgram'),
+ qsparse = require('querystring').parse,
+ http = require('http');
+
+
+var writeconfig = function(text,worker,cb,obj){
+ temp.open({suffix: '-statsdconf.js'}, function(err, info) {
+ if (err) throw err;
+ fs.writeSync(info.fd, text);
+ fs.close(info.fd, function(err) {
+ if (err) throw err;
+ worker(info.path,cb,obj);
+ });
+ });
+}
+
+var array_contents_are_equal = function(first,second){
+ var intlen = _.intersection(first,second).length;
+ var unlen = _.union(first,second).length;
+ return (intlen == unlen) && (intlen == first.length);
+}
+
+var statsd_send = function(data,sock,host,port,cb){
+ send_data = new Buffer(data);
+ sock.send(send_data,0,send_data.length,port,host,function(err,bytes){
+ if (err) {
+ throw err;
+ }
+ cb();
+ });
+}
+
+// keep collecting data until a specified timeout period has elapsed
+// this will let us capture all data chunks so we don't miss one
+var collect_for = function(server,timeout,cb){
+ var received = [];
+ var in_flight = 0;
+ var timed_out = false;
+ var collector = function(req,res){
+ in_flight += 1;
+ var body = '';
+ req.on('data',function(data){ body += data; });
+ req.on('end',function(){
+ received = received.concat(body.split("\n"));
+ in_flight -= 1;
+ if((in_flight < 1) && timed_out){
+ server.removeListener('request',collector);
+ cb(received);
+ }
+ });
+ }
+
+ setTimeout(function (){
+ timed_out = true;
+ if((in_flight < 1)) {
+ server.removeListener('connection',collector);
+ cb(received);
+ }
+ },timeout);
+
+ server.on('connection',collector);
+}
+
+module.exports = {
+ setUp: function (callback) {
+ this.testport = 31337;
+ this.myflush = 200;
+ var configfile = "{graphService: \"graphite\"\n\
+ , batch: 200 \n\
+ , flushInterval: " + this.myflush + " \n\
+ , percentThreshold: 90\n\
+ , port: 8125\n\
+ , dumpMessages: false \n\
+ , debug: false\n\
+ , graphitePort: " + this.testport + "\n\
+ , graphiteHost: \"127.0.0.1\"}";
+
+ this.acceptor = net.createServer();
+ this.acceptor.listen(this.testport);
+ this.sock = dgram.createSocket('udp4');
+
+ this.server_up = true;
+ this.ok_to_die = false;
+ this.exit_callback_callback = process.exit;
+
+ writeconfig(configfile,function(path,cb,obj){
+ obj.path = path;
+ obj.server = spawn('node',['stats.js', path]);
+ obj.exit_callback = function (code) {
+ obj.server_up = false;
+ if(!obj.ok_to_die){
+ console.log('node server unexpectedly quit with code: ' + code);
+ process.exit(1);
+ }
+ else {
+ obj.exit_callback_callback();
+ }
+ };
+ obj.server.on('exit', obj.exit_callback);
+ obj.server.stderr.on('data', function (data) {
+ console.log('stderr: ' + data.toString().replace(/\n$/,''));
+ });
+ /*
+ obj.server.stdout.on('data', function (data) {
+ console.log('stdout: ' + data.toString().replace(/\n$/,''));
+ });
+ */
+ obj.server.stdout.on('data', function (data) {
+ // wait until server is up before we finish setUp
+ if (data.toString().match(/server is up/)) {
+ cb();
+ }
+ });
+
+ },callback,this);
+ },
+ tearDown: function (callback) {
+ this.sock.close();
+ this.acceptor.close();
+ this.ok_to_die = true;
+ if(this.server_up){
+ this.exit_callback_callback = callback;
+ this.server.kill();
+ } else {
+ callback();
+ }
+ },
+
+ send_well_formed_posts: function (test) {
+ test.expect(2);
+
+ // we should integrate a timeout into this
+ this.acceptor.once('connection',function(c){
+ var body = '';
+ c.on('data',function(d){ body += d; });
+ c.on('end',function(){
+ var rows = body.split("\n");
+ var entries = _.map(rows, function(x) {
+ var chunks = x.split(' ');
+ var data = {};
+ data[chunks[0]] = chunks[1];
+ return data;
+ });
+ test.ok(_.include(_.map(entries,function(x) { return _.keys(x)[0] }),'statsd.numStats'),'graphite output includes numStats');
+ test.equal(_.find(entries, function(x) { return _.keys(x)[0] == 'statsd.numStats' })['statsd.numStats'],2);
+ test.done();
+ });
+ });
+ },
+
+ timers_are_valid: function (test) {
+ test.expect(3);
+
+ var testvalue = 100;
+ var me = this;
+ this.acceptor.once('connection',function(c){
+ statsd_send('a_test_value:' + testvalue + '|ms',me.sock,'127.0.0.1',8125,function(){
+ collect_for(me.acceptor,me.myflush*2,function(strings){
+ test.ok(strings.length > 0,'should receive some data');
+ var hashes = _.map(strings, function(x) {
+ var chunks = x.split(' ');
+ var data = {};
+ data[chunks[0]] = chunks[1];
+ return data;
+ });
+ var numstat_test = function(post){
+ var mykey = 'statsd.numStats';
+ return _.include(_.keys(post),mykey) && (post[mykey] == 3);
+ };
+ test.ok(_.any(hashes,numstat_test), 'statsd.numStats should be 1');
+
+ var testtimervalue_test = function(post){
+ var mykey = 'stats.timers.a_test_value.mean_90';
+ return _.include(_.keys(post),mykey) && (post[mykey] == testvalue);
+ };
+ test.ok(_.any(hashes,testtimervalue_test), 'stats.timers.a_test_value.mean should be ' + testvalue);
+
+ test.done();
+ });
+ });
+ });
+ },
+
+ counts_are_valid: function (test) {
+ test.expect(4);
+
+ var testvalue = 100;
+ var me = this;
+ this.acceptor.once('connection',function(c){
+ statsd_send('a_test_value:' + testvalue + '|c',me.sock,'127.0.0.1',8125,function(){
+ collect_for(me.acceptor,me.myflush*2,function(strings){
+ test.ok(strings.length > 0,'should receive some data');
+ var hashes = _.map(strings, function(x) {
+ var chunks = x.split(' ');
+ var data = {};
+ data[chunks[0]] = chunks[1];
+ return data;
+ });
+ var numstat_test = function(post){
+ var mykey = 'statsd.numStats';
+ return _.include(_.keys(post),mykey) && (post[mykey] == 3);
+ };
+ test.ok(_.any(hashes,numstat_test), 'statsd.numStats should be 1');
+
+ var testavgvalue_test = function(post){
+ var mykey = 'stats.a_test_value';
+ return _.include(_.keys(post),mykey) && (post[mykey] == (testvalue/(me.myflush / 1000)));
+ };
+ test.ok(_.any(hashes,testavgvalue_test), 'stats.a_test_value should be ' + (testvalue/(me.myflush / 1000)));
+
+ var testcountvalue_test = function(post){
+ var mykey = 'stats_counts.a_test_value';
+ return _.include(_.keys(post),mykey) && (post[mykey] == testvalue);
+ };
+ test.ok(_.any(hashes,testcountvalue_test), 'stats_counts.a_test_value should be ' + testvalue);
+
+ test.done();
+ });
+ });
+ });
+ }
+}
View
25 test/graphite_tests.js
@@ -79,6 +79,7 @@ module.exports = {
, port: 8125\n\
, dumpMessages: false \n\
, debug: false\n\
+ , graphite: { legacyNamespace: false }\n\
, graphitePort: " + this.testport + "\n\
, graphiteHost: \"127.0.0.1\"}";
@@ -148,8 +149,8 @@ module.exports = {
data[chunks[0]] = chunks[1];
return data;
});
- test.ok(_.include(_.map(entries,function(x) { return _.keys(x)[0] }),'statsd.numStats'),'graphite output includes numStats');
- test.equal(_.find(entries, function(x) { return _.keys(x)[0] == 'statsd.numStats' })['statsd.numStats'],2);
+ test.ok(_.include(_.map(entries,function(x) { return _.keys(x)[0] }),'stats.statsd.numStats'),'graphite output includes numStats');
+ test.equal(_.find(entries, function(x) { return _.keys(x)[0] == 'stats.statsd.numStats' })['stats.statsd.numStats'],2);
test.done();
});
});
@@ -171,16 +172,16 @@ module.exports = {
return data;
});
var numstat_test = function(post){
- var mykey = 'statsd.numStats';
+ var mykey = 'stats.statsd.numStats';
return _.include(_.keys(post),mykey) && (post[mykey] == 2);
};
test.ok(_.any(hashes,numstat_test), 'statsd.numStats should be 0');
var bad_lines_seen_value_test = function(post){
- var mykey = 'stats_counts.statsd.bad_lines_seen';
+ var mykey = 'stats.counters.statsd.bad_lines_seen.count';
return _.include(_.keys(post),mykey) && (post[mykey] == testvalue);
};
- test.ok(_.any(hashes,bad_lines_seen_value_test), 'stats_counts.statsd.bad_lines_seen should be ' + testvalue);
+ test.ok(_.any(hashes,bad_lines_seen_value_test), 'stats.counters.statsd.bad_lines_seen.count should be ' + testvalue);
test.done();
});
@@ -204,10 +205,10 @@ module.exports = {
return data;
});
var numstat_test = function(post){
- var mykey = 'statsd.numStats';
+ var mykey = 'stats.statsd.numStats';
return _.include(_.keys(post),mykey) && (post[mykey] == 3);
};
- test.ok(_.any(hashes,numstat_test), 'statsd.numStats should be 1');
+ test.ok(_.any(hashes,numstat_test), 'stats.statsd.numStats should be 1');
var testtimervalue_test = function(post){
var mykey = 'stats.timers.a_test_value.mean_90';
@@ -237,22 +238,22 @@ module.exports = {
return data;
});
var numstat_test = function(post){
- var mykey = 'statsd.numStats';
+ var mykey = 'stats.statsd.numStats';
return _.include(_.keys(post),mykey) && (post[mykey] == 3);
};
test.ok(_.any(hashes,numstat_test), 'statsd.numStats should be 3');
var testavgvalue_test = function(post){
- var mykey = 'stats.a_test_value';
+ var mykey = 'stats.counters.a_test_value.rate';
return _.include(_.keys(post),mykey) && (post[mykey] == (testvalue/(me.myflush / 1000)));
};
- test.ok(_.any(hashes,testavgvalue_test), 'stats.a_test_value should be ' + (testvalue/(me.myflush / 1000)));
+ test.ok(_.any(hashes,testavgvalue_test), 'a_test_value.rate should be ' + (testvalue/(me.myflush / 1000)));
var testcountvalue_test = function(post){
- var mykey = 'stats_counts.a_test_value';
+ var mykey = 'stats.counters.a_test_value.count';
return _.include(_.keys(post),mykey) && (post[mykey] == testvalue);
};
- test.ok(_.any(hashes,testcountvalue_test), 'stats_counts.a_test_value should be ' + testvalue);
+ test.ok(_.any(hashes,testcountvalue_test), 'a_test_value.count should be ' + testvalue);
test.done();
});
Something went wrong with that request. Please try again.