Skip to content
This repository has been archived by the owner on Sep 19, 2024. It is now read-only.

Add event method support #59

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# node-statsd

A node.js client for [Etsy](http://etsy.com)'s [StatsD](https://github.com/etsy/statsd) server.
A node.js client for [Etsy](http://etsy.com)'s [StatsD](https://github.com/etsy/statsd) server and
Datadog's [DogStatsD](http://docs.datadoghq.com/guides/dogstatsd/) server.

This client will let you fire stats at your StatsD server from a node.js application.

Expand Down Expand Up @@ -28,7 +29,7 @@ Parameters (specified as an options hash):
* `mock`: Create a mock StatsD instance, sending no stats to the server? `default: false`
* `global_tags`: Optional tags that will be added to every metric `default: []`

All StatsD methods have the same API:
All StatsD methods other than event have the same API:
* `name`: Stat name `required`
* `value`: Stat value `required except in increment/decrement where it defaults to 1/-1 respectively`
* `sampleRate`: Sends only a sample of data to StatsD `default: 1`
Expand All @@ -37,6 +38,20 @@ All StatsD methods have the same API:

If an array is specified as the `name` parameter each item in that array will be sent along with the specified value.

The event method has the following API:

* `title`: Event title `required`
* `text`: Event description `default is title`
* `options`: Options for the event
* `date_happened` Assign a timestamp to the event `default is now`
* `hostname` Assign a hostname to the event.
* `aggregation_key` Assign an aggregation key to the event, to group it with some others.
* `priority` Can be ‘normal’ or ‘low’ `default: normal`
* `source_type_name` Assign a source type to the event.
* `alert_type` Can be ‘error’, ‘warning’, ‘info’ or ‘success’ `default: info`
* `tags`: The Array of tags to add to metrics `default: []`
* `callback`: The callback to execute once the metric has been sent

```javascript
var StatsD = require('node-statsd'),
client = new StatsD();
Expand All @@ -60,6 +75,9 @@ If an array is specified as the `name` parameter each item in that array will be
client.set('my_unique', 'foobar');
client.unique('my_unique', 'foobarbaz');

// Event: sends the titled event
client.event('my_title', 'description');

// Incrementing multiple items
client.increment(['these', 'are', 'different', 'stats']);

Expand Down Expand Up @@ -89,6 +107,14 @@ If an array is specified as the `name` parameter each item in that array will be
client.histogram('my_histogram', 42, 0.25, ['tag'], next);
```

## DogStatsD-specific usage

Some of the functionality mentioned above is specific to DogStatsD and will not do anything if are using the regular statsd client. This includes:
* global_tags parameter
* tags parameter
* histogram method
* event method

## Errors

In the event that there is a socket error, `node-statsd` will allow this error to bubble up. If you would like to catch the errors, just attach a listener to the socket property on the instance.
Expand Down
75 changes: 69 additions & 6 deletions lib/statsd.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,63 @@ Client.prototype.set = function (stat, value, sampleRate, tags, callback) {
this.sendAll(stat, value, 's', sampleRate, tags, callback);
};

/**
* Send on an event
* @param title {String} The title of the event
* @param text {String} The description of the event. Optional- title is used if not given.
* @param options
* @option date_happened {Date} Assign a timestamp to the event. Default is now.
* @option hostname {String} Assign a hostname to the event.
* @option aggregation_key {String} Assign an aggregation key to the event, to group it with some others.
* @option priority {String} Can be ‘normal’ or ‘low’. Default is 'normal'.
* @option source_type_name {String} Assign a source type to the event.
* @option alert_type {String} Can be ‘error’, ‘warning’, ‘info’ or ‘success’. Default is 'info'.
* @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.event = function(title, text, options, tags, callback) {
var message,
msgTitle = title ? title : '',
msgText = text ? text : title;

// start out the message with the event-specific title and text info
message = '_e{' + msgTitle.length + ',' + msgText.length + '}:' + msgTitle + '|' + msgText;

// add in the event-specific options
if (options) {
if (options.date_happened && options.date_happened instanceof Date) {
message += '|d:' + options.date_happened.getTime();
}
if (options.hostname) {
message += '|h:' + options.hostname;
}
if (options.aggregation_key) {
message += '|k:' + options.aggregation_key;
}
if (options.priority) {
message += '|p:' + options.priority;
}
if (options.source_type_name) {
message += '|s:' + options.source_type_name;
}
if (options.alert_type) {
message += '|t:' + options.alert_type;
}
}

// allow for tags to be omitted and callback to be used in its place
if(typeof tags === 'function' && callback === undefined) {
callback = tags;
}

this.send(message, tags, callback);
};

/**
* Checks if stats is an array and sends all stats calling back once all have sent
* @param stat {String|Array} The stat(s) to send
* @param value The value to send
* @param type The type of the metric
* @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.
Expand Down Expand Up @@ -175,10 +228,10 @@ Client.prototype.sendAll = function(stat, value, type, sampleRate, tags, callbac

if(Array.isArray(stat)){
stat.forEach(function(item){
self.send(item, value, type, sampleRate, tags, onSend);
self.sendStat(item, value, type, sampleRate, tags, onSend);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to split the existing send method in two, and the part that is applicable to both events and stats is now called send().

});
} else {
this.send(stat, value, type, sampleRate, tags, callback);
this.sendStat(stat, value, type, sampleRate, tags, callback);
}
};

Expand All @@ -191,10 +244,8 @@ Client.prototype.sendAll = function(stat, value, type, sampleRate, tags, callbac
* @param tags {Array} The Array of tags to add to metrics
* @param callback {Function=} Callback when message is done being delivered. Optional.
*/
Client.prototype.send = function (stat, value, type, sampleRate, tags, callback) {
var message = this.prefix + stat + this.suffix + ':' + value + '|' + type,
buf,
merged_tags = [];
Client.prototype.sendStat = function (stat, value, type, sampleRate, tags, callback) {
var message = this.prefix + stat + this.suffix + ':' + value + '|' + type;

if(sampleRate && sampleRate < 1){
if(Math.random() < sampleRate){
Expand All @@ -204,6 +255,18 @@ Client.prototype.send = function (stat, value, type, sampleRate, tags, callback)
return;
}
}
this.send(message, tags, callback);
}

/**
* Send a stat or event across the wire
* @param message {String} The constructed message without tags
* @param tags {Array} The tags to include (along with global tags). Optional.
* @param callback {Function=} Callback when message is done being delivered. Optional.
*/
Client.prototype.send = function (message, tags, callback) {
var buf,
merged_tags = [];

if(tags && Array.isArray(tags)){
merged_tags = merged_tags.concat(tags);
Expand Down
6 changes: 1 addition & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,5 @@
, "devDependencies": {
"mocha":"*"
}
, "licenses" :
[ { "type" : "MIT"
, "url" : "http://github.com/sivy/node-stats/raw/master/LICENSE"
}
]
, "license" : "MIT"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated, and if you want this in a separate PR (or just don't like it for some reason) I can remove it. It's just something I noticed when doing an npm install that there was a warning. npm has switched to this license format as described in https://docs.npmjs.com/files/package.json

}
87 changes: 87 additions & 0 deletions test/test_statsd.js
Original file line number Diff line number Diff line change
Expand Up @@ -680,4 +680,91 @@ describe('StatsD', function(){
});
});

describe('#event', function(finished) {
it('should send proper event format for title and text', function (finished) {
udpTest(function (message, server) {
assert.equal(message, '_e{4,11}:test|description');
server.close();
finished();
}, function (server) {
var address = server.address(),
statsd = new StatsD(address.address, address.port);

statsd.event('test', 'description');
});
});

it('should reuse the title when when text is missing', function (finished) {
udpTest(function (message, server) {
assert.equal(message, '_e{4,4}:test|test');
server.close();
finished();
}, function (server) {
var address = server.address(),
statsd = new StatsD(address.address, address.port);

statsd.event('test');
});
});

it('should send proper event format for title, text, and options', function (finished) {
var date = new Date();
udpTest(function (message, server) {
assert.equal(message, '_e{10,12}:test title|another desc|d:' + date.getTime() +
'|h:host|k:ag_key|p:low|s:source_type|t:warning');
server.close();
finished();
}, function (server) {
var address = server.address(),
statsd = new StatsD(address.address, address.port),
options = {
date_happened: date,
hostname: 'host',
aggregation_key: 'ag_key',
priority: 'low',
source_type_name: 'source_type',
alert_type: 'warning'
};

statsd.event('test title', 'another desc', options);
});
});

it('should send proper event format for title, text, some options, and tags', function (finished) {
udpTest(function (message, server) {
assert.equal(message, '_e{10,12}:test title|another desc|h:host|#foo,bar');
server.close();
finished();
}, function (server) {
var address = server.address(),
statsd = new StatsD(address.address, address.port),
options = {
hostname: 'host'
};

statsd.event('test title', 'another desc', options, ['foo', 'bar']);
});
});

it('should send proper event format for title, text, tags, and a callback', function (finished) {
var called = true;
udpTest(function (message, server) {
assert.equal(message, '_e{10,12}:test title|another desc|#foo,bar');
assert.equal(called, true);
server.close();
finished();
}, function (server) {
var address = server.address(),
statsd = new StatsD(address.address, address.port);

statsd.event('test title', 'another desc', null, ['foo', 'bar'], function(){
called = true;
});
});
});

it('should send no event stat when a mock Client is used', function(finished){
assertMockClientMethod('event', finished);
});
});
});