1 | var 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 | */
|
16 | var 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 | */
|
60 | Client.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 | */
|
72 | Client.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 | */
|
84 | Client.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 | */
|
96 | Client.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 | */
|
109 | Client.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 | */
|
121 | Client.prototype.unique =
|
122 | Client.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 | */
|
134 | Client.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 | */
|
191 | Client.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 | */
|
222 | Client.prototype.close = function(){
|
223 | this.socket.close();
|
224 | }
|
225 |
|
226 | exports = module.exports = Client;
|
227 | exports.StatsD = Client;
|