├── .gitignore ├── History.md ├── Makefile ├── Readme.md ├── examples ├── counters.js ├── gauges.js ├── server.js ├── tcp.js ├── timers-sugar.js └── timers.js ├── index.js ├── package.json └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 0.2.0 / 2014-04-25 3 | ================== 4 | 5 | * add TCP support 6 | 7 | 0.1.1 / 2014-04-25 8 | ================== 9 | 10 | * change histogram |h to |ms since most statsd implementations seem to not support it 11 | 12 | 0.1.0 / 2014-04-15 13 | ================== 14 | 15 | * add .set() 16 | 17 | 0.0.5 / 2014-04-15 18 | ================== 19 | 20 | * Revert "remove histogram" 21 | 22 | 0.0.3 / 2014-04-15 23 | ================== 24 | 25 | * fix sock.send() for old crazy node that does not allow strings 26 | 27 | 0.0.2 / 2014-04-15 28 | ================== 29 | 30 | * fix debug() label 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | test: 3 | @./node_modules/.bin/mocha \ 4 | --require should \ 5 | --reporter dot \ 6 | --bail 7 | 8 | .PHONY: test -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # statsy 3 | 4 | A simple statsd client. 5 | 6 | ## Installation 7 | 8 | ``` 9 | $ npm install statsy 10 | ``` 11 | 12 | ## Example 13 | 14 | ```js 15 | 16 | var Client = require('statsy'); 17 | var http = require('http'); 18 | var stats = new Client; 19 | 20 | setInterval(function(){ 21 | stats.incr('requests'); 22 | var end = stats.timer('request'); 23 | http.get('http://yahoo.com', function(err, res){ 24 | // do stuff 25 | end(); 26 | }); 27 | }, 1000); 28 | 29 | ``` 30 | 31 | ## API 32 | 33 | ### Client([opts]) 34 | 35 | Initialize a client with the given options: 36 | 37 | - `host` [localhost] 38 | - `port` [8125] 39 | - `prefix` optional prefix ('.' is appended) 40 | - `tcp` use TCP instead of UDP 41 | 42 | Events from the socket are forwarded, however by default 43 | errors are simply ignored. When TCP is used reconnection 44 | attempts will be made until the connection is re-established. 45 | 46 | ### .gauge(name, val) 47 | 48 | Send gauge value. 49 | 50 | ### .meter(name, val) 51 | 52 | Send meter value. 53 | 54 | ### .set(name, val) 55 | 56 | Send set value. 57 | 58 | ### .count(name, val) 59 | 60 | Send count value. 61 | 62 | ### .incr(name, [val]) 63 | 64 | Increment by `val` or 1. 65 | 66 | ### .decr(name, [val]) 67 | 68 | Decrement by `val` or 1. 69 | 70 | ### .histogram(name, val) 71 | 72 | Send histogram value. 73 | 74 | ### .histogram(name) 75 | 76 | Return histogram delta function. 77 | 78 | ### .timer(name, val) 79 | 80 | Send timer value. 81 | 82 | ### .timer(name) 83 | 84 | Return timer delta function. 85 | 86 | # License 87 | 88 | MIT -------------------------------------------------------------------------------- /examples/counters.js: -------------------------------------------------------------------------------- 1 | 2 | var Client = require('..'); 3 | require('./server'); 4 | 5 | var stats = new Client; 6 | 7 | setInterval(function(){ 8 | stats.count('something', 1); 9 | }, 150); 10 | 11 | setInterval(function(){ 12 | stats.count('something', 50); 13 | }, 500); -------------------------------------------------------------------------------- /examples/gauges.js: -------------------------------------------------------------------------------- 1 | 2 | var Client = require('..'); 3 | require('./server'); 4 | 5 | var stats = new Client({ prefix: 'myapp' }); 6 | 7 | setInterval(function(){ 8 | var mem = process.memoryUsage(); 9 | stats.gauge('heap:used', mem.heapUsed); 10 | }, 1000); -------------------------------------------------------------------------------- /examples/server.js: -------------------------------------------------------------------------------- 1 | 2 | var dgram = require('dgram'); 3 | var sock = dgram.createSocket('udp4'); 4 | 5 | sock.bind(8125); 6 | 7 | sock.on('message', function(msg){ 8 | console.log('statsd: %j', msg.toString()); 9 | }); -------------------------------------------------------------------------------- /examples/tcp.js: -------------------------------------------------------------------------------- 1 | 2 | var Client = require('..'); 3 | var http = require('http'); 4 | var net = require('net'); 5 | 6 | var stats = new Client({ tcp: true }); 7 | 8 | net.createServer(function(sock){ 9 | console.log('client connected %j', sock.address()); 10 | sock.pipe(process.stdout); 11 | setTimeout(function(){ 12 | console.log('close'); 13 | sock.destroy(); 14 | }, 2000); 15 | }).listen(8125); 16 | 17 | setInterval(function(){ 18 | var start = new Date; 19 | http.get('http://yahoo.com', function(err, res){ 20 | stats.timer('request', new Date - start); 21 | }); 22 | }, 300); -------------------------------------------------------------------------------- /examples/timers-sugar.js: -------------------------------------------------------------------------------- 1 | 2 | var Client = require('..'); 3 | var http = require('http'); 4 | var stats = new Client; 5 | 6 | require('./server'); 7 | 8 | setInterval(function(){ 9 | var end = stats.timer('request'); 10 | http.get('http://yahoo.com', function(err, res){ 11 | end(); 12 | }); 13 | }, 1000); 14 | 15 | -------------------------------------------------------------------------------- /examples/timers.js: -------------------------------------------------------------------------------- 1 | 2 | var Client = require('..'); 3 | var http = require('http'); 4 | var stats = new Client; 5 | 6 | require('./server'); 7 | 8 | setInterval(function(){ 9 | var start = new Date; 10 | http.get('http://yahoo.com', function(err, res){ 11 | stats.timer('request', new Date - start); 12 | }); 13 | }, 300); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Emitter = require('events').EventEmitter; 7 | var debug = require('debug')('statsy'); 8 | var fwd = require('forward-events'); 9 | var finished = require('finished'); 10 | var Backoff = require('backo'); 11 | var assert = require('assert'); 12 | var dgram = require('dgram'); 13 | var net = require('net'); 14 | var url = require('url'); 15 | 16 | /** 17 | * Expose `Client`. 18 | */ 19 | 20 | module.exports = Client; 21 | 22 | /** 23 | * Initialize a new `Client` with `opts`. 24 | * 25 | * @param {Object} [opts] 26 | * @api public 27 | */ 28 | 29 | function Client(opts) { 30 | if (!(this instanceof Client)) return new Client(opts); 31 | opts = opts || {}; 32 | this.host = opts.host || 'localhost'; 33 | this.port = opts.port || 8125; 34 | this.tcp = opts.tcp; 35 | this.prefix = opts.prefix; 36 | this.backoff = new Backoff; 37 | this.on('error', this.onerror.bind(this)); 38 | this.connect(); 39 | } 40 | 41 | /** 42 | * Inherit from `Emitter.prototype`. 43 | */ 44 | 45 | Client.prototype.__proto__ = Emitter.prototype; 46 | 47 | /** 48 | * Noop errors. 49 | */ 50 | 51 | Client.prototype.onerror = function(err){ 52 | debug('error %s', err.stack); 53 | }; 54 | 55 | /** 56 | * Reconnect TCP. 57 | * 58 | * @api private 59 | */ 60 | 61 | Client.prototype.reconnect = function(){ 62 | var ms = this.backoff.duration(); 63 | debug('connection lost, reconnecting in %sms', ms); 64 | setTimeout(this.connect.bind(this), ms); 65 | }; 66 | 67 | /** 68 | * Connect via TCP or UDP. 69 | * 70 | * @api private 71 | */ 72 | 73 | Client.prototype.connect = function(){ 74 | if (this.tcp) { 75 | this.sock = net.connect({ host: this.host, port: this.port }); 76 | this.sock.on('connect', this.backoff.reset.bind(this.backoff)); 77 | finished(this.sock, this.reconnect.bind(this)); 78 | } else { 79 | this.sock = dgram.createSocket('udp4'); 80 | fwd(this.sock, this); 81 | } 82 | }; 83 | 84 | /** 85 | * Send `msg`. 86 | * 87 | * @param {String} msg 88 | * @api private 89 | */ 90 | 91 | Client.prototype.send = function(msg){ 92 | var sock = this.sock; 93 | 94 | if (this.tcp) { 95 | if (sock.writable) sock.write(msg + '\n'); 96 | } else { 97 | var buf = new Buffer(msg); 98 | sock.send(buf, 0, buf.length, this.port, this.host); 99 | } 100 | }; 101 | 102 | /** 103 | * Send with prefix when specified. 104 | * 105 | * @param {String} msg 106 | * @api private 107 | */ 108 | 109 | Client.prototype.write = function(msg){ 110 | if (this.prefix) msg = this.prefix + '.' + msg; 111 | this.send(msg); 112 | }; 113 | 114 | /** 115 | * Send a gauge value. 116 | * 117 | * @param {String} name 118 | * @param {Number} val 119 | * @api public 120 | */ 121 | 122 | Client.prototype.gauge = function(name, val){ 123 | debug('gauge %j %s', name, val); 124 | this.write(name + ':' + val + '|g'); 125 | }; 126 | 127 | /** 128 | * Send a set value. 129 | * 130 | * @param {String} name 131 | * @param {Number} val 132 | * @api public 133 | */ 134 | 135 | Client.prototype.set = function(name, val){ 136 | debug('set %j %s', name, val); 137 | this.write(name + ':' + val + '|s'); 138 | }; 139 | 140 | /** 141 | * Send a meter value. 142 | * 143 | * @param {String} name 144 | * @param {Number} val 145 | * @api public 146 | */ 147 | 148 | Client.prototype.meter = function(name, val){ 149 | debug('meter %j %s', name, val); 150 | this.write(name + ':' + val + '|m'); 151 | }; 152 | 153 | /** 154 | * Send a timer value or omit the value 155 | * to return a completion function. 156 | * 157 | * @param {String} name 158 | * @param {Number} [val] 159 | * @return {Function} 160 | * @api public 161 | */ 162 | 163 | Client.prototype.timer = function(name, val){ 164 | var self = this; 165 | 166 | if (1 == arguments.length) { 167 | var start = new Date; 168 | return function(){ 169 | self.timer(name, new Date - start); 170 | } 171 | } 172 | 173 | debug('timer %j %s', name, val); 174 | this.write(name + ':' + val + '|ms'); 175 | }; 176 | 177 | /** 178 | * Send a histogram value or omit the value 179 | * to return a completion function. 180 | * 181 | * @param {String} name 182 | * @param {Number} [val] 183 | * @return {Function} 184 | * @api public 185 | */ 186 | 187 | Client.prototype.histogram = function(name, val){ 188 | var self = this; 189 | 190 | if (1 == arguments.length) { 191 | var start = new Date; 192 | return function(){ 193 | self.histogram(name, new Date - start); 194 | } 195 | } 196 | 197 | debug('histogram %j %s', name, val); 198 | this.write(name + ':' + val + '|ms'); 199 | }; 200 | 201 | /** 202 | * Send a counter value with optional sample rate. 203 | * 204 | * @param {String} name 205 | * @param {Number} val 206 | * @param {Number} sample 207 | * @api public 208 | */ 209 | 210 | Client.prototype.count = function(name, val, sample){ 211 | debug('count %j %s sample=%s', name, val, sample); 212 | if (sample) { 213 | this.write(name + ':' + val + '|c|@' + sample); 214 | } else { 215 | this.write(name + ':' + val + '|c'); 216 | } 217 | }; 218 | 219 | /** 220 | * Increment counter by `val` or 1. 221 | * 222 | * @param {String} name 223 | * @param {Number} val 224 | * @api public 225 | */ 226 | 227 | Client.prototype.incr = function(name, val){ 228 | if (null == val) val = 1; 229 | this.count(name, val); 230 | }; 231 | 232 | /** 233 | * Decrement counter by `val` or 1. 234 | * 235 | * @param {String} name 236 | * @param {Number} val 237 | * @api public 238 | */ 239 | 240 | Client.prototype.decr = function(name, val){ 241 | if (null == val) val = 1; 242 | this.count(name, -val); 243 | }; 244 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "statsy", 3 | "version": "0.2.0", 4 | "repository": "segmentio/statsy", 5 | "description": "statsd client", 6 | "keywords": [ 7 | "statsd" 8 | ], 9 | "dependencies": { 10 | "debug": "~0.8.0", 11 | "forward-events": "0.0.1", 12 | "backo": "~1.0.1", 13 | "finished": "~1.1.2" 14 | }, 15 | "devDependencies": { 16 | "mocha": "*", 17 | "should": "*" 18 | }, 19 | "license": "MIT", 20 | "files": [ 21 | "index.js" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 2 | var something = require('..'); 3 | 4 | describe('something', function(){ 5 | 6 | }) --------------------------------------------------------------------------------