Repository: brightcove/hot-shots
Pull request #64 (2018-04-26 21:28:21)

Add distribution support
Distributions are a new metric type in Agent 6 that aggregate the values that are sent from multiple hosts during a flush interval to measure statistical distributions across your entire infrastructure https://www.datadoghq.com/blog/summary-graphs-metric-graphs-101/#distributions


Patch:
diff --git a/CHANGES.md b/CHANGES.md
index 666927c..ae8f2e7 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,9 @@
 CHANGELOG
 =========
 
+## 5.4.0 (2018-4-26)
+* @RobGraham Added `distribution()` support for DataDog v6
+
 ## 5.3.0 (2018-4-3)
 * @tanelso2 Added support for using default route on Linux
 
diff --git a/README.md b/README.md
index 69239f7..56b78f2 100644
--- a/README.md
+++ b/README.md
@@ -106,6 +106,9 @@ The check method has the following API:
   // Histogram: send data for histogram stat (DataDog and Telegraf only)
   client.histogram('my_histogram', 42);
 
+  // Distribution: Tracks the statistical distribution of a set of values across your infrastructure. (DataDog v6)
+  client.distribution('my_distribution', 42);
+
   // Gauge: Gauge a stat by a specified amount
   client.gauge('my_gauge', 123.45);
 
diff --git a/lib/statsd.js b/lib/statsd.js
index 46eb8e0..94d034f 100644
--- a/lib/statsd.js
+++ b/lib/statsd.js
@@ -195,6 +195,18 @@ Client.prototype.histogram = function (stat, value, sampleRate, tags, callback)
   this.sendAll(stat, value, 'h', sampleRate, tags, callback);
 };
 
+/**
+ * Represents the distribution stat
+ * @param stat {String|Array} The stat(s) to send
+ * @param value The value to send
+ * @param sampleRate {Number=} The Number of times to sample (0 to 1). Optional.
+ * @param tags {Array=} The Array of tags to add to metrics. Optional.
+ * @param callback {Function=} Callback when message is done being delivered. Optional.
+ */
+Client.prototype.distribution = function (stat, value, sampleRate, tags, callback) {
+  this.sendAll(stat, value, 'd', sampleRate, tags, callback);
+};
+
 
 /**
  * Gauges a stat by a specified amount
diff --git a/package.json b/package.json
index 337f614..d96e32d 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "hot-shots",
   "description": "Node.js client for StatsD, DogStatsD, and Telegraf",
-  "version": "5.3.0",
+  "version": "5.4.0",
   "author": "Steve Ivy",
   "types": "./types.d.ts",
   "contributors": [
diff --git a/types.d.ts b/types.d.ts
index cc08e7e..4c84bd0 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -75,6 +75,7 @@ declare module "hot-shots" {
 
     timing(stat: string | string[], value: number, sampleRate?: number, tags?: Tags, callback?: StatsCb): void;
     histogram(stat: string | string[], value: number, sampleRate?: number, tags?: Tags, callback?: StatsCb): void;
+    distribution(stat: string | string[], value: number, sampleRate?: number, tags?: Tags, callback?: StatsCb): void;
     gauge(stat: string | string[], value: number, sampleRate?: number, tags?: Tags, callback?: StatsCb): void;
     set(stat: string | string[], value: number, sampleRate?: number, tags?: Tags, callback?: StatsCb): void;
     unique(stat: string | string[], value: number, sampleRate?: number, tags?: Tags, callback?: StatsCb): void;


Test patch:
diff --git a/test/test_statsd.js b/test/test_statsd.js
index 741b67f..d325342 100644
--- a/test/test_statsd.js
+++ b/test/test_statsd.js
@@ -740,6 +740,114 @@ function doTests(StatsD) {
     });
 
   });
+  
+  describe('#distribution', function(){
+    it('should send proper distribution format without prefix, suffix, sampling and callback', function(finished){
+      udpTest(function(message, server){
+        assert.equal(message, 'test:42|d');
+        server.close();
+        finished();
+      }, function(server){
+        var address = server.address(),
+            statsd = new StatsD(address.address, address.port);
+
+        statsd.distribution('test', 42);
+      });
+    });
+
+    it('should send proper distribution format with tags', function(finished){
+      udpTest(function(message, server){
+        assert.equal(message, 'test:42|d|#foo,bar');
+        server.close();
+        finished();
+      }, function(server){
+        var address = server.address(),
+            statsd = new StatsD(address.address, address.port);
+
+        statsd.distribution('test', 42, ['foo', 'bar']);
+      });
+    });
+
+    it('should send proper distribution format with prefix, suffix, sampling and callback', function(finished){
+      var called = false;
+      udpTest(function(message, server){
+        assert.equal(message, 'foo.test.bar:42|d|@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.distribution('test', 42, 0.5, function(){
+          called = true;
+        });
+      });
+    });
+
+    it('should properly send a and b with the same value', function(finished){
+      var called = 0,
+          messageNumber = 0;
+
+      udpTest(function(message, server){
+        if(messageNumber === 0){
+          assert.equal(message, 'a:42|d');
+          messageNumber += 1;
+        } else {
+          assert.equal(message, 'b:42|d');
+          server.close();
+          finished();
+        }
+      }, function(server){
+        var address = server.address(),
+            statsd = new StatsD(address.address, address.port);
+
+        statsd.distribution(['a', 'b'], 42, null, function(error, bytes){
+          called += 1;
+          assert.ok(called === 1); //ensure it only gets called once
+          assert.equal(error, null);
+          assert.equal(bytes, 12);
+        });
+      });
+    });
+
+    it('should send no distribution stat when a mock Client is used', function(finished){
+      assertMockClientMethod('distribution', finished);
+    });
+
+    it('should call callback after distribution call', function(finished){
+      udpTest(function(message, server){
+        assert.equal(message, 'test:42|d');
+        server.close();
+      }, function(server){
+        var address = server.address(),
+          statsd = new StatsD(address.address, address.port);
+
+        statsd.distribution('test', 42, null, null, function(err, data) {
+          assert.equal(data, 9);
+          finished();
+        });
+      });
+    });
+
+    it('should have error in callback after bad distribution call', function(finished){
+      udpTest(function(message, server){
+        throw new Error('should not be called');
+      }, function(server){
+        var address = server.address(),
+          statsd = new StatsD(address.address, address.port);
+
+        statsd.close(function() {
+          statsd.distribution('test', 42, null, null, function(err, data) {
+            assert.ok(err !== undefined);
+            assert.ok(data === undefined);
+            finished();
+          });
+        });
+      });
+    });
+
+  });
 
   describe('#gauge', function(){
     it('should send proper gauge format without prefix, suffix, sampling and callback', function(finished){