Permalink
Browse files

Initial commit.

  • Loading branch information...
0 parents commit 1bff25ce0e3830baaa9cccaaeba6eee04495374b Charlie committed Jul 1, 2012
Showing with 186 additions and 0 deletions.
  1. +35 −0 README.md
  2. +151 −0 hostedgraphite.js
@@ -0,0 +1,35 @@
+# Hosted Graphite statsd plugin
+
+This is a plugin for [etsy's statsd](https://github.com/etsy/statsd) that sends your metric data to the [Hosted Graphite](http://www.hostedgraphite.com) service.
+
+## Installation
+
+Place **hostedgraphite.js** into the **backends/** directory of your **statsd** install.
+
+## Configuration
+
+### Enabling the plugin
+
+Add './backends/hostedgraphite' to the ```backends``` list.
+
+```
+ backends: ['./backends/hostedgraphite']
+```
+
+### Setting the Hosted Graphite key
+
+Set ```hostedGraphiteAPIKey``` to your key. This looks like a UUID and is available from your account details page on [hostedgraphite.com](hostedgraphite.com)
+
+```
+ hostedGraphiteAPIKey: 'deadbeef-dead-beef-dead-beefdeadbeef'
+```
+
+## Example configuration
+
+```
+{
+ port: 8125
+ , backends: ['./backends/hostedgraphite']
+ , hostedGraphiteAPIKey: 'deadbeef-dead-beef-dead-beefdeadbeef'
+}
+```
@@ -0,0 +1,151 @@
+/*
+ * Flush stats to hostedgraphite (http://www.hostedgraphite.com).
+ *
+ * To enable this backend, include './backends/hostedgraphite' in the backends
+ * configuration array:
+ *
+ * backends: ['./backends/hostedgraphite']
+ *
+ * This backend supports the following config options:
+ *
+ * hostedGraphiteAPIKey: A hostedgraphite.com API key. (a UUID)
+ */
+
+var net = require('net'),
+ util = require('util'),
+ http = require('http');
+
+var debug;
+var flushInterval;
+var APIKey;
+
+var graphiteStats = {};
+
+var post_stats = function graphite_post_stats(statString) {
+ var options = {
+ host: 'www.hostedgraphite.com',
+ port: 80,
+ path: '/api/v1/sink',
+ method: 'POST',
+ auth: APIKey,
+ headers: {'Content-Length': statString.length}
+ };
+
+ var req = http.request(options, function(res) {
+ if (res.statusCode == 202) {
+ graphiteStats.last_flush = Math.round(new Date().getTime() / 1000);
+ }
+ });
+
+ req.on('error', function(ex) {
+ graphiteStats.last_exception = Math.round(new Date().getTime() / 1000);
+ if (debug) {
+ util.log(ex);
+ }
+ });
+
+ req.write(statString);
+ req.end();
+}
+
+var flush_stats = function graphite_flush(ts, metrics) {
+ var statString = '';
+ var numStats = 0;
+ var key;
+
+ var counters = metrics.counters;
+ var gauges = metrics.gauges;
+ var timers = metrics.timers;
+ var pctThreshold = metrics.pctThreshold;
+
+ for (key in counters) {
+ var value = counters[key];
+ var valuePerSecond = value / (flushInterval / 1000); // calculate "per second" rate
+
+ statString += 'stats.' + key + ' ' + valuePerSecond + ' ' + ts + "\n";
+ statString += 'stats_counts.' + key + ' ' + value + ' ' + ts + "\n";
+
+ numStats += 1;
+ }
+
+ for (key in timers) {
+ if (timers[key].length > 0) {
+ var values = timers[key].sort(function (a,b) { return a-b; });
+ var count = values.length;
+ var min = values[0];
+ var max = values[count - 1];
+
+ var cumulativeValues = [min];
+ for (var i = 1; i < count; i++) {
+ cumulativeValues.push(values[i] + cumulativeValues[i-1]);
+ }
+
+ var sum = min;
+ var mean = min;
+ var maxAtThreshold = max;
+
+ var message = "";
+
+ var key2;
+
+ for (key2 in pctThreshold) {
+ var pct = pctThreshold[key2];
+ if (count > 1) {
+ var thresholdIndex = Math.round(((100 - pct) / 100) * count);
+ var numInThreshold = count - thresholdIndex;
+
+ maxAtThreshold = values[numInThreshold - 1];
+ sum = cumulativeValues[numInThreshold - 1];
+ mean = sum / numInThreshold;
+ }
+
+ var clean_pct = '' + pct;
+ clean_pct.replace('.', '_');
+ message += 'stats.timers.' + key + '.mean_' + clean_pct + ' ' + mean + ' ' + ts + "\n";
+ message += 'stats.timers.' + key + '.upper_' + clean_pct + ' ' + maxAtThreshold + ' ' + ts + "\n";
+ message += 'stats.timers.' + key + '.sum_' + clean_pct + ' ' + sum + ' ' + ts + "\n";
+ }
+
+ sum = cumulativeValues[count-1];
+ mean = sum / count;
+
+ message += 'stats.timers.' + key + '.upper ' + max + ' ' + ts + "\n";
+ message += 'stats.timers.' + key + '.lower ' + min + ' ' + ts + "\n";
+ message += 'stats.timers.' + key + '.count ' + count + ' ' + ts + "\n";
+ message += 'stats.timers.' + key + '.sum ' + sum + ' ' + ts + "\n";
+ message += 'stats.timers.' + key + '.mean ' + mean + ' ' + ts + "\n";
+ statString += message;
+
+ numStats += 1;
+ }
+ }
+
+ for (key in gauges) {
+ statString += 'stats.gauges.' + key + ' ' + gauges[key] + ' ' + ts + "\n";
+ numStats += 1;
+ }
+
+ statString += 'statsd.numStats ' + numStats + ' ' + ts + "\n";
+ post_stats(statString);
+};
+
+var backend_status = function graphite_status(writeCb) {
+ for (stat in graphiteStats) {
+ writeCb(null, 'hostedgraphite', stat, graphiteStats[stat]);
+ }
+};
+
+exports.init = function graphite_init(startup_time, config, events) {
+ debug = config.debug;
+ APIKey = config.hostedGraphiteAPIKey;
+
+ graphiteStats.last_flush = startup_time;
+ graphiteStats.last_exception = startup_time;
+
+ flushInterval = config.flushInterval;
+
+ events.on('flush', flush_stats);
+ events.on('status', backend_status);
+
+ return true;
+};

0 comments on commit 1bff25c

Please sign in to comment.