UNPKG

7.58 kBJavaScriptView Raw
1var dgram = require('dgram'),
2 dns = require('dns');
3
4/**
5 * The UDP Client for StatsD
6 * @param options
7 * @option host {String} The host to connect to default: localhost
8 * @option port {String|Integer} The port to connect to default: 8125
9 * @option prefix {String} An optional prefix to assign to each stat name sent
10 * @option suffix {String} An optional suffix to assign to each stat name sent
11 * @option globalize {boolean} An optional boolean to add "statsd" as an object in the global namespace
12 * @option cacheDns {boolean} An optional option to only lookup the hostname -> ip address once
13 * @option mock {boolean} An optional boolean indicating this Client is a mock object, no stats are sent.
14 * @constructor
15 */
16var Client = function (host, port, prefix, suffix, globalize, cacheDns, mock) {
17 var options = host || {},
18 self = this;
19
20 if(arguments.length > 1 || typeof(host) === 'string'){
21 options = {
22 host : host,
23 port : port,
24 prefix : prefix,
25 suffix : suffix,
26 globalize : globalize,
27 cacheDns : cacheDns,
28 mock : mock === true
29 };
30 }
31
32 this.host = options.host || 'localhost';
33 this.port = options.port || 8125;
34 this.prefix = options.prefix || '';
35 this.suffix = options.suffix || '';
36 this.socket = dgram.createSocket('udp4');
37 this.mock = options.mock;
38
39 if(options.cacheDns === true){
40 dns.lookup(options.host, function(err, address, family){
41 if(err == null){
42 self.host = address;
43 }
44 });
45 }
46
47 if(options.globalize){
48 global.statsd = this;
49 }
50};
51
52/**
53 * Represents the timing stat
54 * @param stat {String|Array} The stat(s) to send
55 * @param time {Number} The time in milliseconds to send
56 * @param sampleRate {Number=} The Number of times to sample (0 to 1). Optional.
57 * @param tags {Array=} The Array of tags to add to metrics. Optional.
58 * @param callback {Function=} Callback when message is done being delivered. Optional.
59 */
60Client.prototype.timing = function (stat, time, sampleRate, tags, callback) {
61 this.sendAll(stat, time, 'ms', sampleRate, tags, callback);
62};
63
64/**
65 * Increments a stat by a specified amount
66 * @param stat {String|Array} The stat(s) to send
67 * @param value The value to send
68 * @param sampleRate {Number=} The Number of times to sample (0 to 1). Optional.
69 * @param tags {Array=} The Array of tags to add to metrics. Optional.
70 * @param callback {Function=} Callback when message is done being delivered. Optional.
71 */
72Client.prototype.increment = function (stat, value, sampleRate, tags, callback) {
73 this.sendAll(stat, value || 1, 'c', sampleRate, tags, callback);
74};
75
76/**
77 * Decrements a stat by a specified amount
78 * @param stat {String|Array} The stat(s) to send
79 * @param value The value to send
80 * @param sampleRate {Number=} The Number of times to sample (0 to 1). Optional.
81 * @param tags {Array=} The Array of tags to add to metrics. Optional.
82 * @param callback {Function=} Callback when message is done being delivered. Optional.
83 */
84Client.prototype.decrement = function (stat, value, sampleRate, tags, callback) {
85 this.sendAll(stat, -value || -1, 'c', sampleRate, tags, callback);
86};
87
88/**
89 * Represents the histogram stat
90 * @param stat {String|Array} The stat(s) to send
91 * @param value The value to send
92 * @param sampleRate {Number=} The Number of times to sample (0 to 1). Optional.
93 * @param tags {Array=} The Array of tags to add to metrics. Optional.
94 * @param callback {Function=} Callback when message is done being delivered. Optional.
95 */
96Client.prototype.histogram = function (stat, value, sampleRate, tags, callback) {
97 this.sendAll(stat, value, 'h', sampleRate, tags, callback);
98};
99
100
101/**
102 * Gauges a stat by a specified amount
103 * @param stat {String|Array} The stat(s) to send
104 * @param value The value to send
105 * @param sampleRate {Number=} The Number of times to sample (0 to 1). Optional.
106 * @param tags {Array=} The Array of tags to add to metrics. Optional.
107 * @param callback {Function=} Callback when message is done being delivered. Optional.
108 */
109Client.prototype.gauge = function (stat, value, sampleRate, tags, callback) {
110 this.sendAll(stat, value, 'g', sampleRate, tags, callback);
111};
112
113/**
114 * Counts unique values by a specified amount
115 * @param stat {String|Array} The stat(s) to send
116 * @param value The value to send
117 * @param sampleRate {Number=} The Number of times to sample (0 to 1). Optional.
118 * @param tags {Array=} The Array of tags to add to metrics. Optional.
119 * @param callback {Function=} Callback when message is done being delivered. Optional.
120 */
121Client.prototype.unique =
122Client.prototype.set = function (stat, value, sampleRate, tags, callback) {
123 this.sendAll(stat, value, 's', sampleRate, tags, callback);
124};
125
126/**
127 * Checks if stats is an array and sends all stats calling back once all have sent
128 * @param stat {String|Array} The stat(s) to send
129 * @param value The value to send
130 * @param sampleRate {Number=} The Number of times to sample (0 to 1). Optional.
131 * @param tags {Array=} The Array of tags to add to metrics. Optional.
132 * @param callback {Function=} Callback when message is done being delivered. Optional.
133 */
134Client.prototype.sendAll = function(stat, value, type, sampleRate, tags, callback){
135 var completed = 0,
136 calledback = false,
137 sentBytes = 0,
138 self = this;
139
140 if(sampleRate && typeof sampleRate !== 'number'){
141 callback = tags;
142 tags = sampleRate;
143 sampleRate = undefined;
144 }
145
146 if(tags && !Array.isArray(tags)){
147 callback = tags;
148 tags = undefined;
149 }
150
151 /**
152 * Gets called once for each callback, when all callbacks return we will
153 * call back from the function
154 * @private
155 */
156 function onSend(error, bytes){
157 completed += 1;
158 if(calledback || typeof callback !== 'function'){
159 return;
160 }
161
162 if(error){
163 calledback = true;
164 return callback(error);
165 }
166
167 sentBytes += bytes;
168 if(completed === stat.length){
169 callback(null, sentBytes);
170 }
171 }
172
173 if(Array.isArray(stat)){
174 stat.forEach(function(item){
175 self.send(item, value, type, sampleRate, tags, onSend);
176 });
177 } else {
178 this.send(stat, value, type, sampleRate, tags, callback);
179 }
180};
181
182/**
183 * Sends a stat across the wire
184 * @param stat {String|Array} The stat(s) to send
185 * @param value The value to send
186 * @param type {String} The type of message to send to statsd
187 * @param sampleRate {Number} The Number of times to sample (0 to 1)
188 * @param tags {Array} The Array of tags to add to metrics
189 * @param callback {Function=} Callback when message is done being delivered. Optional.
190 */
191Client.prototype.send = function (stat, value, type, sampleRate, tags, callback) {
192 var message = this.prefix + stat + this.suffix + ':' + value + '|' + type,
193 buf;
194
195 if(sampleRate && sampleRate < 1){
196 if(Math.random() < sampleRate){
197 message += '|@' + sampleRate;
198 } else {
199 //don't want to send if we don't meet the sample ratio
200 return;
201 }
202 }
203
204 if(tags && Array.isArray(tags)){
205 message += '|#' + tags.join(',');
206 }
207
208 // Only send this stat if we're not a mock Client.
209 if(!this.mock) {
210 buf = new Buffer(message);
211 this.socket.send(buf, 0, buf.length, this.port, this.host, callback);
212 } else {
213 if(typeof callback === 'function'){
214 callback(null, 0);
215 }
216 }
217};
218
219/**
220 * Close the underlying socket and stop listening for data on it.
221 */
222Client.prototype.close = function(){
223 this.socket.close();
224}
225
226exports = module.exports = Client;
227exports.StatsD = Client;