rack-statsd_batch 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Rack::StatsdBatch
2
+
3
+ Throw your metrics data into your request env, and on egress it will get pushed
4
+ to statsd in as few requests as possible.
5
+
6
+ # Setup
7
+
8
+ Add to your gemfile
9
+
10
+ gem 'rack-statsd_batch', '~> 0.0.1', require: 'rack/statsd_batch'
11
+
12
+ Add to your middleware in config/application.rb
13
+
14
+ config.middleware.use Rack::StatsdBatch, 'statsd-server.myhost.com', 1234
15
+
16
+ # Use
17
+
18
+ The stats collector is included in your request environment. Access it from your
19
+ controller:
20
+
21
+ metrics = request.env['metrics']
22
+
23
+ There are five message types that map to the statsd backend
24
+
25
+ metrics.gauge_diff 'registered-users', +1
26
+ metrics.gauge 'total_things', 2037
27
+ metrics.timing 'render_time_ms', 237
28
+ metrics.count 'things_done', 1
29
+ metrics.sets 'user_ids', 27
@@ -0,0 +1,89 @@
1
+ require 'socket'
2
+
3
+ module Rack
4
+ class StatsdBatch
5
+ def initialize rack_app, hostname, port, mtu=1432
6
+ @app = rack_app
7
+ @hostname = hostname
8
+ @port = port
9
+ @mtu = mtu
10
+ end
11
+
12
+ def call env
13
+ env['metrics'] = Rack::StatsdBatch::Recorder.new
14
+ status, headers, response = @app.call(env)
15
+ env['metrics'].publish(@hostname, @port, @mtu)
16
+ [status, headers, response]
17
+ end
18
+
19
+ class Recorder
20
+ attr_reader :data
21
+
22
+ def initialize
23
+ @data = []
24
+ end
25
+
26
+ def count key, diff
27
+ @data << "#{key}:#{diff}|c"
28
+ end
29
+
30
+ def timing key, ms
31
+ @data << "#{key}:#{ms}|ms"
32
+ end
33
+
34
+ def gauge key, value
35
+ @data << "#{key}:#{value}|g"
36
+ end
37
+
38
+ def gauge_diff key, diff
39
+ value = "#{diff}"
40
+ value = "+#{value}" if diff > 0
41
+ @data << "#{key}:#{value}|g"
42
+ end
43
+
44
+ def sets key, set_entry
45
+ @data << "#{key}:#{set_entry}|s"
46
+ end
47
+
48
+ def publish host, port, mtu
49
+ return if @data.empty?
50
+ c = connection(host, port)
51
+ build_packets(mtu).each {|p| c.send(p) }
52
+ end
53
+
54
+ private
55
+
56
+ def connection host, port
57
+ Rack::StatsdBatch::Connection.new(UDPSocket.new, host, port)
58
+ end
59
+
60
+ def build_packets mtu
61
+ rv = []
62
+ packet = ''
63
+ while !data.empty?
64
+ message = data.shift
65
+ newline_length = packet.empty? ? 0 : 1
66
+ if packet.length + message.length + newline_length > mtu
67
+ rv << packet
68
+ packet = ''
69
+ end
70
+ message = "\n#{message}" unless packet.empty?
71
+ packet << message
72
+ end
73
+ (rv << packet) unless packet.empty?
74
+ rv
75
+ end
76
+ end
77
+
78
+ class Connection
79
+ def initialize socket, *extra_params
80
+ @socket = socket
81
+ @extras = extra_params
82
+ end
83
+
84
+ def send data
85
+ @socket.send(data, flags=0, *@extras)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,23 @@
1
+ Gem::Specification.new do |s|
2
+ s.specification_version = 2
3
+ s.required_rubygems_version = Gem::Requirement.new(">= 0")
4
+
5
+ s.name = 'rack-statsd_batch'
6
+ s.version = '0.0.1'
7
+ s.date = '2013-12-13'
8
+
9
+ s.summary = 'Send metrics data to statsd after your request'
10
+ s.description = 'Exposes a metrics collector to the request environment and flushes the data to statsd when the request is finished. Batches the data to send as few packets as possible.'
11
+
12
+ s.authors = ['xtoddx (Todd Willey)']
13
+ s.email = 'xtoddx@gmail.com'
14
+ s.homepage = 'http://github.com/CirrusMio/rack-statsd_batch'
15
+ s.license = 'MIT'
16
+
17
+ s.require_paths = %w[lib]
18
+
19
+ s.add_development_dependency('minitest')
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- tests/*`.split("\n")
23
+ end
@@ -0,0 +1,46 @@
1
+ gem 'minitest'
2
+ require 'minitest/autorun'
3
+
4
+ require_relative '../lib/rack/statsd_batch'
5
+
6
+ class ApplicationInterfaceTest < MiniTest::Test
7
+ def setup
8
+ @recorder = Rack::StatsdBatch::Recorder.new
9
+ end
10
+
11
+ def test_count
12
+ @recorder.count('mykey', 1)
13
+ assert_equal 1, @recorder.data.length
14
+ assert_equal 'mykey:1|c', @recorder.data.first
15
+ end
16
+
17
+ def test_timing
18
+ @recorder.timing('mykey', 1)
19
+ assert_equal 1, @recorder.data.length
20
+ assert_equal 'mykey:1|ms', @recorder.data.first
21
+ end
22
+
23
+ def test_gauge_diff_negative
24
+ @recorder.gauge_diff('mykey', 1)
25
+ assert_equal 1, @recorder.data.length
26
+ assert_equal 'mykey:+1|g', @recorder.data.first
27
+ end
28
+
29
+ def test_gauge_diff_negative
30
+ @recorder.gauge_diff('mykey', -1)
31
+ assert_equal 1, @recorder.data.length
32
+ assert_equal 'mykey:-1|g', @recorder.data.first
33
+ end
34
+
35
+ def test_gauge_set
36
+ @recorder.gauge('mykey', 10)
37
+ assert_equal 1, @recorder.data.length
38
+ assert_equal 'mykey:10|g', @recorder.data.first
39
+ end
40
+
41
+ def test_set_multiple_things
42
+ @recorder.gauge('mykey', 1)
43
+ @recorder.gauge_diff('myotherkey', 10)
44
+ assert_equal 2, @recorder.data.length
45
+ end
46
+ end
@@ -0,0 +1,43 @@
1
+ gem 'minitest'
2
+ require 'minitest/autorun'
3
+
4
+ require_relative '../lib/rack/statsd_batch'
5
+
6
+ $statsd_connection = nil
7
+ class TestConnection
8
+ attr_reader :packets
9
+
10
+ def initialize socket, host, port
11
+ $statsd_connection = self
12
+ end
13
+
14
+ def send packet
15
+ (@packets ||= []) << packet
16
+ end
17
+ end
18
+
19
+ class ConnectionTest < MiniTest::Test
20
+ def setup
21
+ @old_const = Rack::StatsdBatch::Connection
22
+ Rack::StatsdBatch.const_set :Connection, TestConnection
23
+ @recorder = Rack::StatsdBatch::Recorder.new
24
+ @recorder.gauge 'mygauge', 1
25
+ @recorder.gauge_diff 'myothergauge', 3
26
+ end
27
+
28
+ def teardown
29
+ Rack::StatsdBatch.const_set :Connection, @old_const
30
+ end
31
+
32
+ def test_sends_packet
33
+ @recorder.publish('hostname', port=1234, mtu=1024)
34
+ assert_equal 1, $statsd_connection.packets.length
35
+ assert_equal "mygauge:1|g\nmyothergauge:+3|g",
36
+ $statsd_connection.packets.first
37
+ end
38
+
39
+ def test_splits_packets
40
+ @recorder.publish('hostname', port=1234, mtu=@recorder.data.first.length)
41
+ assert_equal 2, $statsd_connection.packets.length
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ gem 'minitest'
2
+ require 'minitest/autorun'
3
+
4
+ require_relative '../lib/rack/statsd_batch'
5
+
6
+ class MiddlewareTestApp
7
+ attr_accessor :callback
8
+
9
+ def call env
10
+ if @callback
11
+ @callback.call(env)
12
+ end
13
+ return [{}, 200, '']
14
+ end
15
+ end
16
+
17
+ class MiddlewareTest < MiniTest::Test
18
+ def setup
19
+ @tester = MiddlewareTestApp.new
20
+ @app = Rack::StatsdBatch.new(@tester, 'localhost', 1234)
21
+ end
22
+
23
+ def test_env_contains_recorder
24
+ passed = nil
25
+ @tester.callback = ->(env){ passed = env.include?('metrics') }
26
+ @app.call({})
27
+ assert passed
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-statsd_batch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - xtoddx (Todd Willey)
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-12-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: minitest
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: Exposes a metrics collector to the request environment and flushes the
31
+ data to statsd when the request is finished. Batches the data to send as few packets
32
+ as possible.
33
+ email: xtoddx@gmail.com
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - README.md
39
+ - lib/rack/statsd_batch.rb
40
+ - rack-statsd_batch.gemspec
41
+ - test/application_interface_test.rb
42
+ - test/connection_test.rb
43
+ - test/middleware_test.rb
44
+ homepage: http://github.com/CirrusMio/rack-statsd_batch
45
+ licenses:
46
+ - MIT
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 1.8.25
66
+ signing_key:
67
+ specification_version: 2
68
+ summary: Send metrics data to statsd after your request
69
+ test_files: []
70
+ has_rdoc: