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

Event Support #13

Closed
wants to merge 8 commits into from
Closed
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
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.
Expand Down
79 changes: 66 additions & 13 deletions lib/statsd.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,32 @@ 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;
}
if (options.eventType) {
eventString += '|t:' + options.eventType;
}
if (options.aggKey) {
eventString += '|k:' + options.aggKey;
}
}
eventString += generateTagString(this.global_tags, tags);

self.send_data(new Buffer(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
Expand Down Expand Up @@ -145,29 +171,43 @@ 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);
for (stat in sampled_data) {

sampled_data[stat] = sampled_data[stat] + tagString;

if (Array.isArray(tags))
merged_tags = merged_tags.concat(tags);

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();
Expand All @@ -182,3 +222,16 @@ Client.prototype.close = function() {
};

exports.StatsD = Client;

//Enum types to support events generation
exports.priority = {
NORMAL: 'normal',
LOW: 'low'
};

exports.eventType = {
ERROR: 'error',
WARNING: 'warning',
INFO: 'info',
SUCCESS: 'success'
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"engines": {
"node": ">=0.1.97"
},
"scripts": {"test": "mocha"},
"licenses": [
{
"type": "MIT",
Expand Down
72 changes: 72 additions & 0 deletions test/dogstatsd_events.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* 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() {
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');
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() {
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');
});
});
});
42 changes: 42 additions & 0 deletions test/dogstatsd_metrics.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* 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() {
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');

});
});
});