statsd 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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