diff --git a/lib/statsd.js b/lib/statsd.js index f27383b..5f04cac 100644 --- a/lib/statsd.js +++ b/lib/statsd.js @@ -12,9 +12,10 @@ var dgram = require('dgram'), * @option cacheDns {boolean} An optional option to only lookup the hostname -> ip address once * @option mock {boolean} An optional boolean indicating this Client is a mock object, no stats are sent. * @option global_tags {Array=} Optional tags that will be added to every metric + * @option sampleRate {Float} Global Sampling rate, default: 1 (No sampling) * @constructor */ -var Client = function (host, port, prefix, suffix, globalize, cacheDns, mock, global_tags) { +var Client = function (host, port, prefix, suffix, globalize, cacheDns, mock, global_tags, sampleRate) { var options = host || {}, self = this; @@ -27,7 +28,8 @@ var Client = function (host, port, prefix, suffix, globalize, cacheDns, mock, gl globalize : globalize, cacheDns : cacheDns, mock : mock === true, - global_tags : global_tags + global_tags : global_tags, + sampleRate : sampleRate }; } @@ -38,6 +40,7 @@ var Client = function (host, port, prefix, suffix, globalize, cacheDns, mock, gl this.socket = dgram.createSocket('udp4'); this.mock = options.mock; this.global_tags = options.global_tags || []; + this.sampleRate = options.sampleRate || 1; if(options.cacheDns === true){ dns.lookup(options.host, function(err, address, family){ @@ -196,6 +199,7 @@ Client.prototype.send = function (stat, value, type, sampleRate, tags, callback) buf, merged_tags = []; + sampleRate = sampleRate || this.sampleRate; if(sampleRate && sampleRate < 1){ if(Math.random() < sampleRate){ message += '|@' + sampleRate; @@ -231,7 +235,7 @@ Client.prototype.send = function (stat, value, type, sampleRate, tags, callback) */ Client.prototype.close = function(){ this.socket.close(); -} +}; exports = module.exports = Client; exports.StatsD = Client; diff --git a/test/test_statsd.js b/test/test_statsd.js index 0fbb314..7773f7c 100644 --- a/test/test_statsd.js +++ b/test/test_statsd.js @@ -84,12 +84,13 @@ describe('StatsD', function(){ assert.equal(global.statsd, undefined); assert.equal(statsd.mock, undefined); assert.deepEqual(statsd.global_tags, []); + assert.deepEqual(statsd.sampleRate, 1); assert.ok(!statsd.mock); }); it('should set the proper values when specified', function(){ // cachedDns isn't tested here; see below - var statsd = new StatsD('host', 1234, 'prefix', 'suffix', true, null, true, ['gtag']); + var statsd = new StatsD('host', 1234, 'prefix', 'suffix', true, null, true, ['gtag'], 0.6); assert.equal(statsd.host, 'host'); assert.equal(statsd.port, 1234); assert.equal(statsd.prefix, 'prefix'); @@ -97,6 +98,7 @@ describe('StatsD', function(){ assert.equal(statsd, global.statsd); assert.equal(statsd.mock, true); assert.deepEqual(statsd.global_tags, ['gtag']); + assert.deepEqual(statsd.sampleRate, 0.6); }); it('should set the proper values with options hash format', function(){ @@ -108,7 +110,8 @@ describe('StatsD', function(){ suffix: 'suffix', globalize: true, mock: true, - global_tags: ['gtag'] + global_tags: ['gtag'], + sampleRate: 0.6 }); assert.equal(statsd.host, 'host'); assert.equal(statsd.port, 1234); @@ -117,6 +120,7 @@ describe('StatsD', function(){ assert.equal(statsd, global.statsd); assert.equal(statsd.mock, true); assert.deepEqual(statsd.global_tags, ['gtag']); + assert.deepEqual(statsd.sampleRate, 0.6); }); it('should attempt to cache a dns record if dnsCache is specified', function(done){ @@ -229,6 +233,72 @@ describe('StatsD', function(){ }); }); + describe('#global_sample_rate', function(){ + it('should not sample if it is not specified', function(finished){ + udpTest(function(message, server){ + assert.equal(message, 'test:1|c'); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD(address.address, address.port); + + statsd.increment('test'); + }); + }); + + it('should sample if global sample rate is specified', function(finished){ + var called = false; + udpTest(function(message, server){ + assert.equal(message, 'foo.test.bar:42|c|@0.6'); + assert.equal(called, true); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD(address.address, address.port, 'foo.', '.bar', true, false, false, [], 0.6); + + statsd.increment('test', 42, function(){ + called = true; + }); + }); + }); + + it('should sample if metric sample rate is specified', function(finished){ + var called = false; + udpTest(function(message, server){ + assert.equal(message, 'foo.test.bar:42|c|@0.5'); + assert.equal(called, true); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD(address.address, address.port, 'foo.', '.bar'); + + statsd.increment('test', 42, 0.5, function(){ + called = true; + }); + }); + }); + + it('should honor metric sampleRate if both global and metric sample rate is specified', function(finished){ + var called = false; + udpTest(function(message, server){ + assert.equal(message, 'foo.test.bar:42|c|@0.5'); + assert.equal(called, true); + server.close(); + finished(); + }, function(server){ + var address = server.address(), + statsd = new StatsD(address.address, address.port, 'foo.', '.bar', true, false, false, [], 0.6); + + statsd.increment('test', 42, 0.5, function(){ + called = true; + }); + }); + }); + }); + describe('#timing', function(finished){ it('should send proper time format without prefix, suffix, sampling and callback', function(finished){ udpTest(function(message, server){