From 8f5d1c9fdc99d3934c83be293db1a5b4de04b175 Mon Sep 17 00:00:00 2001 From: James McNally Date: Wed, 27 May 2015 19:41:14 +0100 Subject: [PATCH 1/7] Created basic event support and priority support. --- lib/statsd.js | 26 ++++++++++++++++ package.json | 50 +++++++++++++++++-------------- test/dogstatsd_events.spec.js | 56 +++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 22 deletions(-) create mode 100644 test/dogstatsd_events.spec.js diff --git a/lib/statsd.js b/lib/statsd.js index dbaffaf..36747cb 100644 --- a/lib/statsd.js +++ b/lib/statsd.js @@ -85,6 +85,26 @@ Client.prototype.update_stats = function(stats, delta, sampleRate, tags) { self.send(data, sampleRate, tags); }; +Client.prototype.event = function(title, text, options, tags) { + + var self = this; + + var titleLength = title.length; + var textLength = text.length; + + var eventString = '_e{' + titleLength + ',' + textLength + '}:'; + eventString += title + '|' + text; + + //Start of optional parameters + if(options) { + if (options.priority) { + eventString += '|p:' + options.priority; + } + } + + self.send_data(eventString); +}; + // An internal function update the last time the socket was // used. This function is called when the socket is used // and causes demand allocated ephemeral sockets to be closed @@ -182,3 +202,9 @@ Client.prototype.close = function() { }; exports.StatsD = Client; + +//Enum types to support events generation +exports.priority = { + NORMAL: 'normal', + LOW: 'low' +}; diff --git a/package.json b/package.json index 1549542..a0902c5 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,30 @@ { - "name" : "node-dogstatsd", - "description" : "node client for extended StatsD server of Datadog", - "version" : "0.0.6", - "author" : "Young Han Lee", - "repository" : { - "type" : "git", - "url" : "git://github.com/joybro/node-dogstatsd.git" - }, - "bugs" : { - "url" : "https://github.com/joybro/node-dogstatsd/issues" - }, - "directories" : { - "lib" : "./lib/" - }, - "main" : "./lib/statsd", - "engines" : { "node" : ">=0.1.97" }, - "licenses" : [ - { - "type" : "MIT", - "url" : "http://github.com/joybro/node-dogstatsd/raw/master/LICENSE" - } - ] + "name": "node-dogstatsd", + "description": "node client for extended StatsD server of Datadog", + "version": "0.0.6", + "author": "Young Han Lee", + "repository": { + "type": "git", + "url": "git://github.com/joybro/node-dogstatsd.git" + }, + "bugs": { + "url": "https://github.com/joybro/node-dogstatsd/issues" + }, + "directories": { + "lib": "./lib/" + }, + "main": "./lib/statsd", + "engines": { + "node": ">=0.1.97" + }, + "licenses": [ + { + "type": "MIT", + "url": "http://github.com/joybro/node-dogstatsd/raw/master/LICENSE" + } + ], + "devDependencies": { + "mocha": "^2.2.5", + "sinon": "^1.14.1" + } } diff --git a/test/dogstatsd_events.spec.js b/test/dogstatsd_events.spec.js new file mode 100644 index 0000000..d76760b --- /dev/null +++ b/test/dogstatsd_events.spec.js @@ -0,0 +1,56 @@ +/** + * Created by James McNally on 27/05/2015. + */ + +var assert = require('assert'); +var sinon = require('sinon'); +var dogstatsd = require('../lib/statsd.js'); + + +describe('Dogstatsd Events Functionality', function() { + + + describe('Dogstatd Event Calls', function() { + + var client, sendSpy; + + before(function() { + client = new dogstatsd.StatsD('localhost',8125); + //replace send with spy to test without udp. + sendSpy = sinon.spy(); + client.send_data = sendSpy; + }); + + afterEach(function() { + sendSpy.reset(); + }); + + after(function() { + client.close(); + }); + + it('should send the basic string based on core options', function() { + client.event('TestTitle', 'TestText'); + assert(sendSpy.calledWith('_e{9,8}:TestTitle|TestText')); + }); + + it('should include a priority if included in the options', function() { + + client.event('TestTitle', 'TestText', {priority: dogstatsd.priority.NORMAL}); + var spyBuffer = sendSpy.args[0][0]; + assert(/\|p:normal/.test(spyBuffer)); + }); + }); + + describe('Event Enums', function() { + + it('should translate enumerated types to correct strings for event priorities', function() { + assert.equal(dogstatsd.priority.NORMAL, 'normal'); + assert.equal(dogstatsd.priority.LOW, 'low'); + }); + + it('should translate enumerated types to correct strings for event types', function() { + + }); + }); +}); \ No newline at end of file From 8d39b3360a0364609b10cff3ac7b4db960cb7678 Mon Sep 17 00:00:00 2001 From: James McNally Date: Thu, 28 May 2015 10:20:23 +0100 Subject: [PATCH 2/7] Added support for generating events to datadog through dogstatsd. --- README.md | 19 +++++++++++ lib/statsd.js | 61 +++++++++++++++++++++++++--------- package.json | 1 + test/dogstatsd_events.spec.js | 30 ++++++++++++++++- test/dogstatsd_metrics.spec.js | 46 +++++++++++++++++++++++++ 5 files changed, 141 insertions(+), 16 deletions(-) create mode 100644 test/dogstatsd_metrics.spec.js diff --git a/README.md b/README.md index d19731c..5c5b626 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,25 @@ The name of the package is changed because this isn't really statsd client and s > c.histogram('node_test.some_service.data', 100) // works only with datadog' StatsD > c.increment('node_test.int', 1, ['tag:one']) // works only with datadog' StatsD +### Event Support + +This library supports raising events to a dogstatsd server as well. The convention is: + + client.event(title, description, options, tags) + +Options is an object that contains one of three optional parameters that can be set on the event: + +eventType - There is an enum called eventType that wraps the four options (error, info, success, warning). Default is info. +priority - String that sets priority. There is an enum called priority that wraps the two options (low, normal). Default is normal. +aggKey - String that allows the event viewer to aggregate similar requests. + +#### Example Call + + var dogstatd = require('../lib/statsd.js'); + var client = new dogstatd.StatsD('localhost',8125); + + client.event('Error Detected','error description',{eventType: dogstatd.eventType.ERROR, priority: dogstatd.priority.NORMAL, aggKey: 'Error'},['nodeApp']); + ## License node-statsd is licensed under the MIT license. diff --git a/lib/statsd.js b/lib/statsd.js index 36747cb..a310692 100644 --- a/lib/statsd.js +++ b/lib/statsd.js @@ -100,9 +100,18 @@ Client.prototype.event = function(title, text, options, tags) { if (options.priority) { eventString += '|p:' + options.priority; } + if (options.eventType) { + eventString += '|t:' + options.eventType; + } + if (options.aggKey) { + eventString += '|k:' + options.aggKey; + } + if (tags && tags.length !== 0) { + eventString += generateTagString(this.global_tags, tags); + } } - self.send_data(eventString); + self.send_data(new Buffer(eventString)); }; // An internal function update the last time the socket was @@ -140,7 +149,7 @@ Client.prototype.send_data = function (buf) { this._update_last_used(); - socket.send(buf, 0, buf.length, this.port, this.host); + socket.send(buf, 0, buf.length, this.port, this.host, function() {console.log('Sent buffer: ' + buf)}); }; Client.prototype.send = function(data, sample_rate, tags) { @@ -165,29 +174,44 @@ Client.prototype.send = function(data, sample_rate, tags) { else sampled_data = data; - if (this.global_tags || tags) { - var merged_tags = []; - - if (Array.isArray(this.global_tags)) - merged_tags = merged_tags.concat(this.global_tags); - + var tagString = generateTagString(this.global_tags, tags); + console.log(tagString); + for (stat in sampled_data) { - if (Array.isArray(tags)) - merged_tags = merged_tags.concat(tags); + sampled_data[stat] = sampled_data[stat] + tagString; - if (merged_tags.length > 0) { - var merged_tags_str = merged_tags.join(','); - for (stat in sampled_data) - sampled_data[stat] = sampled_data[stat] + "|#" + merged_tags_str; - } } + for (var stat in sampled_data) { var send_data = stat + ":" + sampled_data[stat]; this.send_data(new Buffer(send_data)); } }; +function generateTagString(globalTags, tags) { + + if (globalTags || tags) { + var merged_tags = []; + + if (Array.isArray(globalTags)) + merged_tags = merged_tags.concat(globalTags); + + + if (Array.isArray(tags)) + merged_tags = merged_tags.concat(tags); + + if (merged_tags.length > 0) { + return '|#' + merged_tags.join(','); + } + else { + return ''; + } + } + //catch all cases where nothing is returned. + return ''; +} + Client.prototype.close = function() { if (this.socket) this.socket.close(); @@ -208,3 +232,10 @@ exports.priority = { NORMAL: 'normal', LOW: 'low' }; + +exports.eventType = { + ERROR: 'error', + WARNING: 'warning', + INFO: 'info', + SUCCESS: 'success' +}; diff --git a/package.json b/package.json index a0902c5..25bfd5f 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "engines": { "node": ">=0.1.97" }, + "scripts": {"test": "mocha"}, "licenses": [ { "type": "MIT", diff --git a/test/dogstatsd_events.spec.js b/test/dogstatsd_events.spec.js index d76760b..343ce4c 100644 --- a/test/dogstatsd_events.spec.js +++ b/test/dogstatsd_events.spec.js @@ -31,7 +31,8 @@ describe('Dogstatsd Events Functionality', function() { it('should send the basic string based on core options', function() { client.event('TestTitle', 'TestText'); - assert(sendSpy.calledWith('_e{9,8}:TestTitle|TestText')); + var spyBuffer = sendSpy.args[0][0]; + assert('_e{9,8}:TestTitle|TestText' === spyBuffer.toString()); }); it('should include a priority if included in the options', function() { @@ -40,6 +41,28 @@ describe('Dogstatsd Events Functionality', function() { var spyBuffer = sendSpy.args[0][0]; assert(/\|p:normal/.test(spyBuffer)); }); + + it('should include an event type if included in the options', function() { + + client.event('TestTitle', 'TestText', {eventType: dogstatsd.eventType.SUCCESS}); + var spyBuffer = sendSpy.args[0][0]; + assert(/\|t:success/.test(spyBuffer)); + }); + + it('should include an aggregation key if included in the options', function() { + + client.event('TestTitle', 'TestText', {aggKey: 'testkey'}); + var spyBuffer = sendSpy.args[0][0]; + assert(/\|k:testkey/.test(spyBuffer)); + }); + + it('should include tags seperated by commas after a |#', function() { + + client.event('TestTitle', 'TestText', {aggKey: 'testkey'},['tag1:test','tag2:test2','tag3']); + var packet = sendSpy.args[0][0]; + assert(/\|#tag1:test,tag2:test2,tag3/.test(packet)); + + }); }); describe('Event Enums', function() { @@ -51,6 +74,11 @@ describe('Dogstatsd Events Functionality', function() { it('should translate enumerated types to correct strings for event types', function() { + assert.equal(dogstatsd.eventType.ERROR,'error'); + assert.equal(dogstatsd.eventType.WARNING,'warning'); + assert.equal(dogstatsd.eventType.INFO,'info'); + assert.equal(dogstatsd.eventType.SUCCESS,'success'); + }); }); }); \ No newline at end of file diff --git a/test/dogstatsd_metrics.spec.js b/test/dogstatsd_metrics.spec.js new file mode 100644 index 0000000..a736fdd --- /dev/null +++ b/test/dogstatsd_metrics.spec.js @@ -0,0 +1,46 @@ +/** + * Created by James on 28/05/2015. + */ + +var assert = require('assert'); +var sinon = require('sinon'); +var dogstatsd = require('../lib/statsd.js'); + +describe('dogstatsd Metrics Functionality: ', function() { + + describe('Tags Functionality', function() { + + var client, sendSpy; + + before(function() { + client = new dogstatsd.StatsD('localhost',8125); + //replace send with spy to test without udp. + sendSpy = sinon.spy(); + client.send_data = sendSpy; + }); + + afterEach(function() { + sendSpy.reset(); + }); + + after(function() { + client.close(); + }); + + it('should include tags separated by commas after a |#', function() { + + client.increment('node_test.int',1,['tag1:test','tag2:test2','tag3']); + var packet = sendSpy.args[0][0]; + assert(/\|#tag1:test,tag2:test2,tag3/.test(packet)); + + }); + + it('should not include the tags section if no tags are specified', function() { + client.increment('node_test.int',1); + var packet = sendSpy.args[0][0]; + console.log(packet.toString()); + assert.equal(packet.toString(),'node_test.int:1|c'); + + }) + }) +}) \ No newline at end of file From 88c45aab422a1f5cd66f4ba208878776b885191e Mon Sep 17 00:00:00 2001 From: James McNally Date: Thu, 28 May 2015 10:25:30 +0100 Subject: [PATCH 3/7] Updated README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5c5b626..629cadc 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,9 @@ This library supports raising events to a dogstatsd server as well. The conventi Options is an object that contains one of three optional parameters that can be set on the event: -eventType - There is an enum called eventType that wraps the four options (error, info, success, warning). Default is info. -priority - String that sets priority. There is an enum called priority that wraps the two options (low, normal). Default is normal. -aggKey - String that allows the event viewer to aggregate similar requests. +* *eventType* - There is an enum called eventType that wraps the four options (error, info, success, warning). Default is info. +* *priority* - String that sets priority. There is an enum called priority that wraps the two options (low, normal). Default is normal. +* *aggKey* - String that allows the event viewer to aggregate similar requests. #### Example Call From a39d06c2e8e547c8eba6fd11518867805775d53d Mon Sep 17 00:00:00 2001 From: Brian Deitte Date: Tue, 4 Aug 2015 12:40:51 -0400 Subject: [PATCH 4/7] Remove the added console.log calls and allow for an undefined options with a defined tags --- lib/statsd.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/statsd.js b/lib/statsd.js index a310692..713d1c1 100644 --- a/lib/statsd.js +++ b/lib/statsd.js @@ -106,9 +106,9 @@ Client.prototype.event = function(title, text, options, tags) { if (options.aggKey) { eventString += '|k:' + options.aggKey; } - if (tags && tags.length !== 0) { - eventString += generateTagString(this.global_tags, tags); - } + } + if (tags && tags.length !== 0) { + eventString += generateTagString(this.global_tags, tags); } self.send_data(new Buffer(eventString)); @@ -149,7 +149,7 @@ Client.prototype.send_data = function (buf) { this._update_last_used(); - socket.send(buf, 0, buf.length, this.port, this.host, function() {console.log('Sent buffer: ' + buf)}); + socket.send(buf, 0, buf.length, this.port, this.host); }; Client.prototype.send = function(data, sample_rate, tags) { @@ -175,7 +175,6 @@ Client.prototype.send = function(data, sample_rate, tags) { sampled_data = data; var tagString = generateTagString(this.global_tags, tags); - console.log(tagString); for (stat in sampled_data) { sampled_data[stat] = sampled_data[stat] + tagString; From 14a0d9dbb8489c52f4d3f96cf77e8da81b538ea9 Mon Sep 17 00:00:00 2001 From: Brian Deitte Date: Thu, 6 Aug 2015 10:21:40 -0400 Subject: [PATCH 5/7] Make sure that global_tags gets sent on even if there are not any tags --- lib/statsd.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/statsd.js b/lib/statsd.js index 713d1c1..a31da5b 100644 --- a/lib/statsd.js +++ b/lib/statsd.js @@ -86,7 +86,6 @@ Client.prototype.update_stats = function(stats, delta, sampleRate, tags) { }; Client.prototype.event = function(title, text, options, tags) { - var self = this; var titleLength = title.length; @@ -107,9 +106,7 @@ Client.prototype.event = function(title, text, options, tags) { eventString += '|k:' + options.aggKey; } } - if (tags && tags.length !== 0) { - eventString += generateTagString(this.global_tags, tags); - } + eventString += generateTagString(this.global_tags, tags); self.send_data(new Buffer(eventString)); }; From 733ce672f52469212300c5632b18a4341a365a63 Mon Sep 17 00:00:00 2001 From: Brian Deitte Date: Thu, 6 Aug 2015 11:13:42 -0400 Subject: [PATCH 6/7] Testing formatting fixup --- test/dogstatsd_events.spec.js | 14 +------------- test/dogstatsd_metrics.spec.js | 12 ++++-------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/test/dogstatsd_events.spec.js b/test/dogstatsd_events.spec.js index 343ce4c..c6a84e5 100644 --- a/test/dogstatsd_events.spec.js +++ b/test/dogstatsd_events.spec.js @@ -1,17 +1,13 @@ /** - * Created by James McNally on 27/05/2015. + * Tests basic events functionality */ var assert = require('assert'); var sinon = require('sinon'); var dogstatsd = require('../lib/statsd.js'); - describe('Dogstatsd Events Functionality', function() { - - describe('Dogstatd Event Calls', function() { - var client, sendSpy; before(function() { @@ -36,49 +32,41 @@ describe('Dogstatsd Events Functionality', function() { }); it('should include a priority if included in the options', function() { - client.event('TestTitle', 'TestText', {priority: dogstatsd.priority.NORMAL}); var spyBuffer = sendSpy.args[0][0]; assert(/\|p:normal/.test(spyBuffer)); }); it('should include an event type if included in the options', function() { - client.event('TestTitle', 'TestText', {eventType: dogstatsd.eventType.SUCCESS}); var spyBuffer = sendSpy.args[0][0]; assert(/\|t:success/.test(spyBuffer)); }); it('should include an aggregation key if included in the options', function() { - client.event('TestTitle', 'TestText', {aggKey: 'testkey'}); var spyBuffer = sendSpy.args[0][0]; assert(/\|k:testkey/.test(spyBuffer)); }); it('should include tags seperated by commas after a |#', function() { - client.event('TestTitle', 'TestText', {aggKey: 'testkey'},['tag1:test','tag2:test2','tag3']); var packet = sendSpy.args[0][0]; assert(/\|#tag1:test,tag2:test2,tag3/.test(packet)); - }); }); describe('Event Enums', function() { - it('should translate enumerated types to correct strings for event priorities', function() { assert.equal(dogstatsd.priority.NORMAL, 'normal'); assert.equal(dogstatsd.priority.LOW, 'low'); }); it('should translate enumerated types to correct strings for event types', function() { - assert.equal(dogstatsd.eventType.ERROR,'error'); assert.equal(dogstatsd.eventType.WARNING,'warning'); assert.equal(dogstatsd.eventType.INFO,'info'); assert.equal(dogstatsd.eventType.SUCCESS,'success'); - }); }); }); \ No newline at end of file diff --git a/test/dogstatsd_metrics.spec.js b/test/dogstatsd_metrics.spec.js index a736fdd..2af3152 100644 --- a/test/dogstatsd_metrics.spec.js +++ b/test/dogstatsd_metrics.spec.js @@ -1,15 +1,12 @@ /** - * Created by James on 28/05/2015. + * Tests basic increment functionality */ - var assert = require('assert'); var sinon = require('sinon'); var dogstatsd = require('../lib/statsd.js'); describe('dogstatsd Metrics Functionality: ', function() { - describe('Tags Functionality', function() { - var client, sendSpy; before(function() { @@ -28,7 +25,6 @@ describe('dogstatsd Metrics Functionality: ', function() { }); it('should include tags separated by commas after a |#', function() { - client.increment('node_test.int',1,['tag1:test','tag2:test2','tag3']); var packet = sendSpy.args[0][0]; assert(/\|#tag1:test,tag2:test2,tag3/.test(packet)); @@ -41,6 +37,6 @@ describe('dogstatsd Metrics Functionality: ', function() { console.log(packet.toString()); assert.equal(packet.toString(),'node_test.int:1|c'); - }) - }) -}) \ No newline at end of file + }); + }); +}); \ No newline at end of file From fb85abf62726c9d27849585f0ff97a51e1a38886 Mon Sep 17 00:00:00 2001 From: Brian Deitte Date: Thu, 6 Aug 2015 11:15:42 -0400 Subject: [PATCH 7/7] Update README to reflect the current state --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 629cadc..ef1e330 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ A node.js client for extended StatsD server of [Datadog](http://www.datadoghq.com). -Datadog added new some features(histogram and tags) to their own StatsD implementation. +Datadog added some new features (histogram, tags, and event) to their own StatsD implementation. This client is an extension of general StatsD client to work with that server. Most parts of codes came from [Steve Ivy](https://github.com/sivy)'s [node-statsd](https://github.com/sivy/node-statsd). -I just added few lines to support datadog's histogram and tags features. +I just added few lines to support datadog's extensions. Some of these changes (histogram and tags) have now made it back into node-statsd. The name of the package is changed because this isn't really statsd client and should be able to be used with original statsd client.