1 | # hot-shots
|
2 |
|
3 | A Node.js client for [Etsy](http://etsy.com)'s [StatsD](https://github.com/etsy/statsd) server, Datadog's [DogStatsD](http://docs.datadoghq.com/guides/dogstatsd/) server, and [InfluxDB's](http://influxdb.com) [Telegraf](https://github.com/influxdb/telegraf) StatsD server.
|
4 |
|
5 | This project was originally a fork off of [node-statsd](https://github.com/sivy/node-statsd). This project
|
6 | includes all changes in the latest node-statsd and many additional changes, including:
|
7 | * TypeScript types
|
8 | * Telegraf support
|
9 | * events
|
10 | * child clients
|
11 | * tcp protocol support
|
12 | * uds (Unix domain socket) protocol support
|
13 | * raw stream protocol support
|
14 | * mock mode
|
15 | * asyncTimer
|
16 | * asyncDistTimer
|
17 | * much more, including many bug fixes
|
18 |
|
19 | hot-shots supports Node 8.x and higher.
|
20 |
|
21 | [![Build Status](https://secure.travis-ci.org/brightcove/hot-shots.png?branch=master)](http://travis-ci.org/brightcove/hot-shots)
|
22 |
|
23 | ## Migrating from node-statsd
|
24 |
|
25 | You should only need to do one thing: change node-statsd to hot-shots in all requires.
|
26 |
|
27 | You can check the detailed [change log](https://github.com/brightcove/hot-shots/blob/master/CHANGES.md) for what has changed since the last release of node-statsd.
|
28 |
|
29 | ## Usage
|
30 |
|
31 | All initialization parameters are optional.
|
32 |
|
33 | Parameters (specified as one object passed into hot-shots):
|
34 |
|
35 | * `host`: The host to send stats to, if not set, the constructor tries to
|
36 | retrieve it from the `DD_AGENT_HOST` environment variable, `default: 'undefined'` which as per [UDP/datagram socket docs](https://nodejs.org/api/dgram.html#dgram_socket_send_msg_offset_length_port_address_callback) results in `127.0.0.1` or `::1` being used.
|
37 | * `port`: The port to send stats to, if not set, the constructor tries to retrieve it from the `DD_DOGSTATSD_PORT` environment variable, `default: 8125`
|
38 | * `prefix`: What to prefix each stat name with `default: ''`
|
39 | * `suffix`: What to suffix each stat name with `default: ''`
|
40 | * `tagPrefix`: Prefix tag list with character `default: '#'`. Note does not work with `telegraf` option.
|
41 | * `tagSeparator`: Separate tags with character `default: ','`. Note does not work with `telegraf` option.
|
42 | * `globalize`: Expose this StatsD instance globally. `default: false`
|
43 | * `cacheDns`: Caches dns lookup to *host* for *cacheDnsTtl*, only used
|
44 | when protocol is `udp`, `default: false`
|
45 | * `cacheDnsTtl`: time-to-live of dns lookups in milliseconds, when *cacheDns* is enabled. `default: 60000`
|
46 | * `mock`: Create a mock StatsD instance, sending no stats to
|
47 | the server and allowing data to be read from mockBuffer. Note that
|
48 | mockBuffer will keep growing, so only use for testing or clear out periodically. `default: false`
|
49 | * `globalTags`: Tags that will be added to every metric. Can be either an object or list of tags. The *Datadog* `dd.internal.entity_id` tag is appended to `globalTags` from the `DD_ENTITY_ID` environment variable if the latter is set. `default: {}`
|
50 | * `maxBufferSize`: If larger than 0, metrics will be buffered and only sent when the string length is greater than the size. `default: 0`
|
51 | * `bufferFlushInterval`: If buffering is in use, this is the time in ms to always flush any buffered metrics. `default: 1000`
|
52 | * `telegraf`: Use Telegraf's StatsD line protocol, which is slightly different than the rest `default: false`
|
53 | * `sampleRate`: Sends only a sample of data to StatsD for all StatsD methods. Can be overridden at the method level. `default: 1`
|
54 | * `errorHandler`: A function with one argument. It is called to handle various errors. `default: none`, errors are thrown/logger to console
|
55 | * `useDefaultRoute`: Use the default interface on a Linux system. Useful when running in containers
|
56 | * `protocol`: Use `tcp` option for TCP protocol, or `uds` for the Unix Domain Socket protocol or `stream` for the raw stream. Defaults to `udp` otherwise.
|
57 | * `path`: Used only when the protocol is `uds`. Defaults to `/var/run/datadog/dsd.socket`.
|
58 | * `stream`: Reference to a stream instance. Used only when the protocol is `stream`.
|
59 | * `udsGracefulErrorHandling`: Used only when the protocol is `uds`. Boolean indicating whether to handle socket errors gracefully. Defaults to true.
|
60 | * `udsGracefulRestartRateLimit`: Used only when the protocol is `uds`. Time (ms) between re-creating the socket. Defaults to `1000`.
|
61 |
|
62 | ### StatsD methods
|
63 | All StatsD methods other than `event`, `close`, and `check` have the same API:
|
64 | * `name`: Stat name `required`
|
65 | * `value`: Stat value `required except in increment/decrement where it defaults to 1/-1 respectively`
|
66 | * `sampleRate`: Sends only a sample of data to StatsD `default: 1`
|
67 | * `tags`: The tags to add to metrics. Can be either an object `{ tag: "value"}` or an array of tags. `default: []`
|
68 | * `callback`: The callback to execute once the metric has been sent or buffered
|
69 |
|
70 | If an array is specified as the `name` parameter each item in that array will be sent along with the specified value.
|
71 |
|
72 | #### `close`
|
73 | The close method has the following API:
|
74 |
|
75 | * `callback`: The callback to execute once close is complete. All other calls to statsd will fail once this is called.
|
76 |
|
77 | #### `event`
|
78 | The event method has the following API:
|
79 |
|
80 | * `title`: Event title `required`
|
81 | * `text`: Event description `default is title`
|
82 | * `options`: Options for the event
|
83 | * `date_happened` Assign a timestamp to the event `default is now`
|
84 | * `hostname` Assign a hostname to the event.
|
85 | * `aggregation_key` Assign an aggregation key to the event, to group it with some others.
|
86 | * `priority` Can be ‘normal’ or ‘low’ `default: normal`
|
87 | * `source_type_name` Assign a source type to the event.
|
88 | * `alert_type` Can be ‘error’, ‘warning’, ‘info’ or ‘success’ `default: info`
|
89 | * `tags`: The tags to add to metrics. Can be either an object `{ tag: "value"}` or an array of tags. `default: []`
|
90 | * `callback`: The callback to execute once the metric has been sent.
|
91 |
|
92 | #### `check`
|
93 | The check method has the following API:
|
94 |
|
95 | * `name`: Check name `required`
|
96 | * `status`: Check status `required`
|
97 | * `options`: Options for the check
|
98 | * `date_happened` Assign a timestamp to the check `default is now`
|
99 | * `hostname` Assign a hostname to the check.
|
100 | * `message` Assign a message to the check.
|
101 | * `tags`: The tags to add to metrics. Can be either an object `{ tag: "value"}` or an array of tags. `default: []`
|
102 | * `callback`: The callback to execute once the metric has been sent.
|
103 |
|
104 | ```javascript
|
105 | var StatsD = require('hot-shots'),
|
106 | client = new StatsD({
|
107 | port: 8020,
|
108 | globalTags: { env: process.env.NODE_ENV },
|
109 | errorHandler: errorHandler,
|
110 | });
|
111 |
|
112 | // Increment: Increments a stat by a value (default is 1)
|
113 | client.increment('my_counter');
|
114 |
|
115 | // Decrement: Decrements a stat by a value (default is -1)
|
116 | client.decrement('my_counter');
|
117 |
|
118 | // Histogram: send data for histogram stat (DataDog and Telegraf only)
|
119 | client.histogram('my_histogram', 42);
|
120 |
|
121 | // Distribution: Tracks the statistical distribution of a set of values across your infrastructure.
|
122 | // (DataDog v6)
|
123 | client.distribution('my_distribution', 42);
|
124 |
|
125 | // Gauge: Gauge a stat by a specified amount
|
126 | client.gauge('my_gauge', 123.45);
|
127 |
|
128 | // Set: Counts unique occurrences of a stat (alias of unique)
|
129 | client.set('my_unique', 'foobar');
|
130 | client.unique('my_unique', 'foobarbaz');
|
131 |
|
132 | // Event: sends the titled event (DataDog only)
|
133 | client.event('my_title', 'description');
|
134 |
|
135 | // Check: sends a service check (DataDog only)
|
136 | client.check('service.up', client.CHECKS.OK, { hostname: 'host-1' }, ['foo', 'bar'])
|
137 |
|
138 | // Incrementing multiple items
|
139 | client.increment(['these', 'are', 'different', 'stats']);
|
140 |
|
141 | // Incrementing with tags
|
142 | client.increment('my_counter', ['foo', 'bar']);
|
143 |
|
144 | // Sampling, this will sample 25% of the time the StatsD Daemon will compensate for sampling
|
145 | client.increment('my_counter', 1, 0.25);
|
146 |
|
147 | // Tags, this will add user-defined tags to the data
|
148 | // (DataDog and Telegraf only)
|
149 | client.histogram('my_histogram', 42, ['foo', 'bar']);
|
150 |
|
151 | // Using the callback. This is the same format for the callback
|
152 | // with all non-close calls
|
153 | client.set(['foo', 'bar'], 42, function(error, bytes){
|
154 | //this only gets called once after all messages have been sent
|
155 | if(error){
|
156 | console.error('Oh noes! There was an error:', error);
|
157 | } else {
|
158 | console.log('Successfully sent', bytes, 'bytes');
|
159 | }
|
160 | });
|
161 |
|
162 | // Timing: sends a timing command with the specified milliseconds
|
163 | client.timing('response_time', 42);
|
164 |
|
165 | // Timing: also accepts a Date object of which the difference is calculated
|
166 | client.timing('response_time', new Date());
|
167 |
|
168 | // Timer: Returns a function that you call to record how long the first
|
169 | // parameter takes to execute (in milliseconds) and then sends that value
|
170 | // using 'client.timing'.
|
171 | // The parameters after the first one (in this case 'fn')
|
172 | // match those in 'client.timing'.
|
173 | var fn = function(a, b) { return a + b };
|
174 | client.timer(fn, 'fn_execution_time')(2, 2);
|
175 |
|
176 | // Async timer: Similar to timer above, but you instead pass in a function
|
177 | // that returns a Promise. And then it returns a Promise that will record the timing.
|
178 | var fn = function () { return new Promise(function (resolve, reject) { setTimeout(resolve, n); }); };
|
179 | var instrumented = statsd.asyncTimer(fn, 'fn_execution_time');
|
180 | instrumented().then(function() {
|
181 | console.log('Code run and metric sent');
|
182 | });
|
183 |
|
184 | // Async timer: Similar to asyncTimer above, but it instead emits a distribution.
|
185 | var fn = function () { return new Promise(function (resolve, reject) { setTimeout(resolve, n); }); };
|
186 | var instrumented = statsd.asyncDistTimer(fn, 'fn_execution_time');
|
187 | instrumented().then(function() {
|
188 | console.log('Code run and metric sent');
|
189 | });
|
190 |
|
191 | // Sampling, tags and callback are optional and could be used in any combination (DataDog and Telegraf only)
|
192 | client.histogram('my_histogram', 42, 0.25); // 25% Sample Rate
|
193 | client.histogram('my_histogram', 42, { tag: 'value'}); // User-defined tag
|
194 | client.histogram('my_histogram', 42, ['tag:value']); // Tags as an array
|
195 | client.histogram('my_histogram', 42, next); // Callback
|
196 | client.histogram('my_histogram', 42, 0.25, ['tag']);
|
197 | client.histogram('my_histogram', 42, 0.25, next);
|
198 | client.histogram('my_histogram', 42, { tag: 'value'}, next);
|
199 | client.histogram('my_histogram', 42, 0.25, { tag: 'value'}, next);
|
200 |
|
201 | // Use a child client to add more context to the client.
|
202 | // Clients can be nested.
|
203 | var childClient = client.childClient({
|
204 | prefix: 'additionalPrefix.',
|
205 | suffix: '.additionalSuffix',
|
206 | globalTags: { globalTag1: 'forAllMetricsFromChildClient'}
|
207 | });
|
208 | childClient.increment('my_counter_with_more_tags');
|
209 |
|
210 | // Close statsd. This will ensure all stats are sent and stop statsd
|
211 | // from doing anything more.
|
212 | client.close(function(err) {
|
213 | console.log('The close did not work quite right: ', err);
|
214 | });
|
215 | ```
|
216 |
|
217 | ## DogStatsD and Telegraf functionality
|
218 |
|
219 | Some of the functionality mentioned above is specific to DogStatsD or Telegraf. They will not do anything if you are using the regular statsd client.
|
220 | * globalTags parameter- DogStatsD or Telegraf
|
221 | * tags parameter- DogStatsD or Telegraf.
|
222 | * telegraf parameter- Telegraf
|
223 | * uds option in protocol parameter- DogStatsD
|
224 | * histogram method- DogStatsD or Telegraf
|
225 | * event method- DogStatsD
|
226 | * check method- DogStatsD
|
227 |
|
228 | ## Errors
|
229 |
|
230 | As usual, callbacks will have an error as their first parameter. You can have an error in both the message and close callbacks.
|
231 |
|
232 | If the optional callback is not given, an error is thrown in some
|
233 | cases and a console.log message is used in others. An error will only
|
234 | be explicitly thrown when there is a missing callback or if it is some potential configuration issue to be fixed.
|
235 |
|
236 | If you would like to ensure all errors are caught, specify an `errorHandler` in your root
|
237 | client. This will catch errors in socket setup, sending of messages,
|
238 | and closing of the socket. If you specify an errorHandler and a callback, the callback will take precedence.
|
239 |
|
240 | ```javascript
|
241 | // Using errorHandler
|
242 | var client = new StatsD({
|
243 | errorHandler: function (error) {
|
244 | console.log("Socket errors caught here: ", error);
|
245 | }
|
246 | })
|
247 | ```
|
248 |
|
249 | ### Congestion error
|
250 |
|
251 | If you get an error like `Error sending hot-shots message: Error: congestion` with an error code of `1`,
|
252 | it is probably because you are sending large volumes of metrics to a single agent/ server.
|
253 | This error only arises when using the UDS protocol and means that packages are being dropped.
|
254 | Take a look at the [Datadog docs](https://docs.datadoghq.com/developers/dogstatsd/high_throughput/?#over-uds-unix-domain-socket) for some tips on tuning your connection.
|
255 |
|
256 | ## Unix domain socket support
|
257 |
|
258 | The 'uds' option as the protocol is to support [Unix Domain Sockets for Datadog](https://docs.datadoghq.com/developers/dogstatsd/unix_socket/). It has the following limitations:
|
259 | - It only works where 'node-gyp' works. If you don't know what this is, this
|
260 | is probably fine for you. If you had an troubles with libraries that
|
261 | you 'node-gyp' before, you will have problems here as well.
|
262 | - It does not work on Windows
|
263 |
|
264 | The above will cause the underlying library that is used, unix-dgram,
|
265 | to not install properly. Given the library is listed as an
|
266 | optionalDependency, and how it's used in the codebase, this install
|
267 | failure will not cause any problems. It only means that you can't use
|
268 | the uds feature.
|
269 |
|
270 | ## Submitting changes
|
271 |
|
272 | Thanks for considering making any updates to this project! Here are the steps to take in your fork:
|
273 |
|
274 | 1. Run "npm install"
|
275 | 2. Add your changes in your fork as well as any new tests needed
|
276 | 3. Run "npm test"
|
277 | 4. Update README.md with any needed documentation
|
278 | 5. If you have made any API changes, update types.d.ts
|
279 | 6. Push your changes and create the PR
|
280 |
|
281 | When you've done all this we're happy to try to get this merged in right away.
|
282 |
|
283 | ## Package versioning and security
|
284 |
|
285 | Versions will attempt to follow semantic versioning, with major changes only coming in major versions.
|
286 |
|
287 | npm publishing is possible by one person, [bdeitte](https://github.com/bdeitte), who has two-factor authentication enabled for publishes. Publishes only contain one additional library, unix-dgram.
|
288 |
|
289 | ## Name
|
290 |
|
291 | Why is this project named hot-shots? Because:
|
292 |
|
293 | 1. It's impossible to find another statsd name on npm
|
294 | 2. It's the name of a dumb movie
|
295 | 3. No good reason
|
296 |
|
297 | ## License
|
298 |
|
299 | hot-shots is licensed under the MIT license.
|