statsd 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/php-example.php ADDED
@@ -0,0 +1,96 @@
1
+ <?php
2
+
3
+ /**
4
+ * Sends statistics to the stats daemon over UDP
5
+ *
6
+ **/
7
+
8
+ class StatsD {
9
+
10
+ /**
11
+ * Log timing information
12
+ *
13
+ * @param string $stats The metric to in log timing info for.
14
+ * @param float $time The ellapsed time (ms) to log
15
+ * @param float|1 $sampleRate the rate (0-1) for sampling.
16
+ **/
17
+ public static function timing($stat, $time, $sampleRate=1) {
18
+ StatsD::send(array($stat => "$time|ms"), $sampleRate);
19
+ }
20
+
21
+ /**
22
+ * Increments one or more stats counters
23
+ *
24
+ * @param string|array $stats The metric(s) to increment.
25
+ * @param float|1 $sampleRate the rate (0-1) for sampling.
26
+ * @return boolean
27
+ **/
28
+ public static function increment($stats, $sampleRate=1) {
29
+ StatsD::updateStats($stats, 1, $sampleRate);
30
+ }
31
+
32
+ /**
33
+ * Decrements one or more stats counters.
34
+ *
35
+ * @param string|array $stats The metric(s) to decrement.
36
+ * @param float|1 $sampleRate the rate (0-1) for sampling.
37
+ * @return boolean
38
+ **/
39
+ public static function decrement($stats, $sampleRate=1) {
40
+ StatsD::updateStats($stats, -1, $sampleRate);
41
+ }
42
+
43
+ /**
44
+ * Updates one or more stats counters by arbitrary amounts.
45
+ *
46
+ * @param string|array $stats The metric(s) to update. Should be either a string or array of metrics.
47
+ * @param int|1 $delta The amount to increment/decrement each metric by.
48
+ * @param float|1 $sampleRate the rate (0-1) for sampling.
49
+ * @return boolean
50
+ **/
51
+ public static function updateStats($stats, $delta=1, $sampleRate=1) {
52
+ if (!is_array($stats)) { $stats = array($stats); }
53
+ $data = array();
54
+ foreach($stats as $stat) {
55
+ $data[$stat] = "$delta|c";
56
+ }
57
+
58
+ StatsD::send($data, $sampleRate);
59
+ }
60
+
61
+ /*
62
+ * Squirt the metrics over UDP
63
+ **/
64
+ public static function send($data, $sampleRate=1) {
65
+ $config = Config::getInstance();
66
+ if (! $config->isEnabled("statsd")) { return; }
67
+
68
+ // sampling
69
+ $sampledData = array();
70
+
71
+ if ($sampleRate < 1) {
72
+ foreach ($data as $stat => $value) {
73
+ if ((mt_rand() / mt_getrandmax()) <= $sampleRate) {
74
+ $sampledData[$stat] = "$value|@$sampleRate";
75
+ }
76
+ }
77
+ } else {
78
+ $sampledData = $data;
79
+ }
80
+
81
+ if (empty($sampledData)) { return; }
82
+
83
+ // Wrap this in a try/catch - failures in any of this should be silently ignored
84
+ try {
85
+ $host = $config->getConfig("statsd.host");
86
+ $port = $config->getConfig("statsd.port");
87
+ $fp = fsockopen("udp://$host", $port, $errno, $errstr);
88
+ if (! $fp) { return; }
89
+ foreach ($sampledData as $stat => $value) {
90
+ fwrite($fp, "$stat:$value");
91
+ }
92
+ fclose($fp);
93
+ } catch (Exception $e) {
94
+ }
95
+ }
96
+ }
data/python_example.py ADDED
@@ -0,0 +1,89 @@
1
+ # python_example.py
2
+
3
+ # Steve Ivy <steveivy@gmail.com>
4
+ # http://monkinetic.com
5
+
6
+ # this file expects local_settings.py to be in the same dir, with statsd host and port information:
7
+ #
8
+ # statsd_host = 'localhost'
9
+ # statsd_port = 8125
10
+
11
+ # Sends statistics to the stats daemon over UDP
12
+ class Statsd(object):
13
+
14
+ @staticmethod
15
+ def timing(stats, time, sample_rate=1):
16
+ """
17
+ Log timing information
18
+ >>> from python_example import Statsd
19
+ >>> Statsd.timing('some.time','500|ms')
20
+ """
21
+ Statsd.update_stats(stats, time, sample_rate)
22
+
23
+ @staticmethod
24
+ def increment(stats, sample_rate=1):
25
+ """
26
+ Increments one or more stats counters
27
+ >>> Statsd.increment('some.int')
28
+ >>> Statsd.increment('some.int',0.5)
29
+ """
30
+ Statsd.update_stats(stats, 1, sample_rate)
31
+
32
+ @staticmethod
33
+ def decrement(stats, sample_rate=1):
34
+ """
35
+ Decrements one or more stats counters
36
+ >>> Statsd.decrement('some.int')
37
+ """
38
+ Statsd.update_stats(stats, -1, sample_rate)
39
+
40
+ @staticmethod
41
+ def update_stats(stats, delta=1, sampleRate=1):
42
+ """
43
+ Updates one or more stats counters by arbitrary amounts
44
+ >>> Statsd.update_stats('some.int',10)
45
+ """
46
+ if (type(stats) is not list):
47
+ stats = [stats]
48
+ data = {}
49
+ for stat in stats:
50
+ data[stat] = "%s|c" % delta
51
+
52
+ Statsd.send(data, sampleRate)
53
+
54
+ @staticmethod
55
+ def send(data, sample_rate=1):
56
+ """
57
+ Squirt the metrics over UDP
58
+ """
59
+ try:
60
+ import local_settings as settings
61
+ host = settings.statsd_host
62
+ port = settings.statsd_port
63
+ addr=(host, port)
64
+ except Error:
65
+ exit(1)
66
+
67
+ sampled_data = {}
68
+
69
+ if(sample_rate < 1):
70
+ import random
71
+ if random.random() <= sample_rate:
72
+ for stat in data.keys():
73
+ value = data[stat]
74
+ sampled_data[stat] = "%s|@%s" %(value, sample_rate)
75
+ else:
76
+ sampled_data=data
77
+
78
+ from socket import *
79
+ udp_sock = socket(AF_INET, SOCK_DGRAM)
80
+ try:
81
+ for stat in sampled_data.keys():
82
+ value = data[stat]
83
+ send_data = "%s:%s" % (stat, value)
84
+ udp_sock.sendto(send_data, addr)
85
+ except:
86
+ import sys
87
+ from pprint import pprint
88
+ print "Unexpected error:", pprint(sys.exc_info())
89
+ pass # we don't care
data/stats.js ADDED
@@ -0,0 +1,128 @@
1
+ var dgram = require('dgram')
2
+ , sys = require('sys')
3
+ , net = require('net')
4
+ , config = require('./config')
5
+
6
+ var counters = {};
7
+ var timers = {};
8
+ var debugInt, flushInt, server;
9
+
10
+ config.configFile(process.argv[2], function (config, oldConfig) {
11
+ if (! config.debug && debugInt) {
12
+ clearInterval(debugInt);
13
+ debugInt = false;
14
+ }
15
+
16
+ if (config.debug) {
17
+ if (debugInt !== undefined) { clearInterval(debugInt); }
18
+ debugInt = setInterval(function () {
19
+ sys.log("Counters:\n" + sys.inspect(counters) + "\nTimers:\n" + sys.inspect(timers));
20
+ }, config.debugInterval || 10000);
21
+ }
22
+
23
+ if (server === undefined) {
24
+ server = dgram.createSocket('udp4', function (msg, rinfo) {
25
+ if (config.dumpMessages) { sys.log(msg.toString()); }
26
+ var bits = msg.toString().split(':');
27
+ var key = bits.shift()
28
+ .replace(/\s+/g, '_')
29
+ .replace(/\//g, '-')
30
+ .replace(/[^a-zA-Z_\-0-9\.]/g, '');
31
+
32
+ if (bits.length == 0) {
33
+ bits.push("1");
34
+ }
35
+
36
+ for (var i = 0; i < bits.length; i++) {
37
+ var sampleRate = 1;
38
+ var fields = bits[i].split("|");
39
+ if (fields[1].trim() == "ms") {
40
+ if (! timers[key]) {
41
+ timers[key] = [];
42
+ }
43
+ timers[key].push(Number(fields[0] || 0));
44
+ } else {
45
+ if (fields[2] && fields[2].match(/^@([\d\.]+)/)) {
46
+ sampleRate = Number(fields[2].match(/^@([\d\.]+)/)[1]);
47
+ }
48
+ if (! counters[key]) {
49
+ counters[key] = 0;
50
+ }
51
+ counters[key] += Number(fields[0] || 1) * (1 / sampleRate);
52
+ }
53
+ }
54
+ });
55
+
56
+ server.bind(config.port || 8125);
57
+
58
+ var flushInterval = Number(config.flushInterval || 10000);
59
+
60
+ flushInt = setInterval(function () {
61
+ var statString = '';
62
+ var ts = Math.round(new Date().getTime() / 1000);
63
+ var numStats = 0;
64
+ var key;
65
+
66
+ for (key in counters) {
67
+ var value = counters[key] / (flushInterval / 1000);
68
+ var message = 'stats.' + key + ' ' + value + ' ' + ts + "\n";
69
+ statString += message;
70
+ counters[key] = 0;
71
+
72
+ numStats += 1;
73
+ }
74
+
75
+ for (key in timers) {
76
+ if (timers[key].length > 0) {
77
+ var pctThreshold = config.percentThreshold || 90;
78
+ var values = timers[key].sort(function (a,b) { return a-b; });
79
+ var count = values.length;
80
+ var min = values[0];
81
+ var max = values[count - 1];
82
+
83
+ var mean = min;
84
+ var maxAtThreshold = max;
85
+
86
+ if (count > 1) {
87
+ var thresholdIndex = Math.round(((100 - pctThreshold) / 100) * count);
88
+ var numInThreshold = count - thresholdIndex;
89
+ values = values.slice(0, numInThreshold);
90
+ maxAtThreshold = values[numInThreshold - 1];
91
+
92
+ // average the remaining timings
93
+ var sum = 0;
94
+ for (var i = 0; i < numInThreshold; i++) {
95
+ sum += values[i];
96
+ }
97
+
98
+ mean = sum / numInThreshold;
99
+ }
100
+
101
+ timers[key] = [];
102
+
103
+ var message = "";
104
+ message += 'stats.timers.' + key + '.mean ' + mean + ' ' + ts + "\n";
105
+ message += 'stats.timers.' + key + '.upper ' + max + ' ' + ts + "\n";
106
+ message += 'stats.timers.' + key + '.upper_' + pctThreshold + ' ' + maxAtThreshold + ' ' + ts + "\n";
107
+ message += 'stats.timers.' + key + '.lower ' + min + ' ' + ts + "\n";
108
+ message += 'stats.timers.' + key + '.count ' + count + ' ' + ts + "\n";
109
+ statString += message;
110
+
111
+ numStats += 1;
112
+ }
113
+ }
114
+
115
+ statString += 'statsd.numStats ' + numStats + ' ' + ts + "\n";
116
+
117
+ var graphite = net.createConnection(config.graphitePort, config.graphiteHost);
118
+
119
+ graphite.on('connect', function() {
120
+ this.write(statString);
121
+ this.end();
122
+ });
123
+
124
+ }, flushInterval);
125
+ }
126
+
127
+ });
128
+
data/statsd.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $:.unshift lib unless $:.include?(lib)
4
+ require File.expand_path("../lib/statsd/server", __FILE__) # for version info
5
+ Gem::Specification.new do |s|
6
+ s.name = "statsd"
7
+ s.version = Statsd::Server::Version
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ['Andrew Coldham', 'Ben VandenBos']
10
+ s.email = ['quasor@me.com']
11
+ s.homepage = "http://github.com/quasor/statsd"
12
+ s.summary = "Ruby version of statsd."
13
+ s.description = "Ruby version of statsd."
14
+
15
+ s.required_rubygems_version = ">= 1.3.6"
16
+
17
+ s.add_dependency "eventmachine", "~> 0.12.10"
18
+ s.add_dependency "mongo", "~> 1.2.0"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
22
+ s.require_path = 'lib'
23
+ end
24
+
data/webapp/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # A sample Gemfile
2
+ source "http://rubygems.org"
3
+
4
+ gem "sinatra"
5
+ gem "mongo"
@@ -0,0 +1,21 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ SystemTimer (1.2.2)
5
+ rack (1.2.1)
6
+ redis (2.1.1)
7
+ sinatra (1.1.3)
8
+ rack (~> 1.1)
9
+ tilt (>= 1.2.2, < 2.0)
10
+ tilt (1.2.2)
11
+ vegas (0.1.8)
12
+ rack (>= 1.0.0)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ SystemTimer
19
+ redis
20
+ sinatra
21
+ vegas
data/webapp/README.md ADDED
@@ -0,0 +1,2 @@
1
+ a simple Sinatra web app for viewing charts
2
+ ruby -Irubygems app.rb
data/webapp/app.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+ require 'mongo'
4
+ require 'yaml'
5
+ ROOT = File.expand_path(File.dirname(__FILE__))
6
+ APP_CONFIG = YAML::load(ERB.new(IO.read(File.join(ROOT,'config.yml'))).result)
7
+ get '/' do
8
+ db = Mongo::Connection.new(APP_CONFIG['dbhost']).db(APP_CONFIG['db'])
9
+ coll = db.collection("stats_10s")
10
+ @stats = coll.find({}).limit(100)
11
+ erb :chart
12
+ end
data/webapp/bin/rackup ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env rbx
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rackup' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rack', 'rackup')
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pathname'
4
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
5
+ Pathname.new(__FILE__).realpath)
6
+ puts ENV['BUNDLE_GEMFILE']
7
+ require 'rubygems'
8
+ require 'bundler/setup'
9
+
10
+ #load Gem.bin_path('statsd', 'statsd')
11
+
12
+ require File.expand_path(File.dirname(__FILE__) + '/../statsd-web.rb')
13
+ require 'vegas'
14
+
15
+ Vegas::Runner.new(StatsdWeb, 'statsd-web')
data/webapp/config.yml ADDED
@@ -0,0 +1,3 @@
1
+ ---
2
+ dbhost: statsd.example.com
3
+ db: statsdb