Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
Charlie committed Jul 1, 2012
0 parents commit 1bff25c
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 0 deletions.
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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'
}
```
151 changes: 151 additions & 0 deletions hostedgraphite.js
Original file line number Diff line number Diff line change
@@ -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.