rack-aggregate 0.1.0

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.
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/.rspec ADDED
File without changes
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,30 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rack-aggregate (0.0.1)
5
+ aggregate
6
+ rack
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ aggregate (0.2.1)
12
+ diff-lcs (1.1.2)
13
+ rack (1.2.1)
14
+ rspec (2.4.0)
15
+ rspec-core (~> 2.4.0)
16
+ rspec-expectations (~> 2.4.0)
17
+ rspec-mocks (~> 2.4.0)
18
+ rspec-core (2.4.0)
19
+ rspec-expectations (2.4.0)
20
+ diff-lcs (~> 1.1.2)
21
+ rspec-mocks (2.4.0)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ aggregate
28
+ rack
29
+ rack-aggregate!
30
+ rspec
@@ -0,0 +1,53 @@
1
+ # Rack Aggregate
2
+
3
+ Statistics aggregator middleware based on [Joseph Ruscio's aggregate gem](https://github.com/josephruscio/aggregate) which provides: minimum, maximum, mean, standard deviation measures of your applications response latency. Best of all, it accomplishes this without recording or storing any of the actual sample values - meaning, by itself it will not impact your performance or memory footprint even across millions of requests.
4
+
5
+ ## Example
6
+
7
+ > ruby examples/rackapp.rb
8
+ > ab -c1 -n20 http://127.0.0.1:4567/ (run 20 requests against the app)
9
+ > ...
10
+ > curl http://127.0.0.1:4567/aggregate
11
+
12
+ count: 20
13
+ mean: 4100.30ms
14
+ min: 1000.11ms
15
+ max: 9000.17ms
16
+ std_dev: 2447.42ms
17
+
18
+ Request histogram (buckets in ms):
19
+ value |------------------------------------------------------------------| count
20
+ 512 |@ | 1
21
+ 1024 |@@@@@@@ | 7
22
+ 2048 |@@@@ | 4
23
+ 4096 |@@@@@@ | 6
24
+ 8192 |@@ | 2
25
+ ~
26
+ Total |------------------------------------------------------------------| 20
27
+
28
+ ### Quickstart with Rack
29
+
30
+ gem install rack-aggregate
31
+
32
+ # in your rack app / rackup file
33
+ require 'rack/aggregate'
34
+ use Rack::Aggregate
35
+
36
+ ### Quickstart with Rails 3
37
+
38
+ # in your Gemfile
39
+ gem 'rack-aggregate', :require => 'rack/aggregate'
40
+
41
+ # in development.rb environment
42
+ config.middleware.use Rack::Aggregate
43
+
44
+ ## Wishlist
45
+
46
+ - Configurable aggregate path
47
+ - Custom sampling rate - no need to log every request
48
+ - Other output formats? JSON?
49
+
50
+ # License
51
+
52
+ (The MIT License)
53
+ Copyright © 2011 Ilya Grigorik
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'rack'
3
+
4
+ $LOAD_PATH.unshift 'lib'
5
+ $LOAD_PATH.unshift 'examples'
6
+
7
+ require 'rack/aggregate'
8
+
9
+ class SomeApp
10
+ def call(env)
11
+ sleep(rand(10))
12
+ [200, {"Content-Type" => "text/plain"}, "Hello World"]
13
+ end
14
+ end
15
+
16
+ builder = Rack::Builder.new do
17
+ use Rack::CommonLogger
18
+ use Rack::Aggregate
19
+
20
+ run SomeApp.new
21
+ end
22
+
23
+ Rack::Handler::Thin.run builder.to_app, :Port => 4567
@@ -0,0 +1,12 @@
1
+ require 'rack'
2
+ require 'aggregate'
3
+
4
+ require 'rack/aggregate/context'
5
+
6
+ module Rack
7
+ module Aggregate
8
+ def self.new(app, options = {}, &blk)
9
+ Context.new(app, options, &blk)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,41 @@
1
+ module Rack
2
+ module Aggregate
3
+ class Context
4
+ AGGREGATE_PATH = /^\/aggregate$/.freeze
5
+
6
+ def initialize(app, options = {}, &blk)
7
+ options = {
8
+ # ...
9
+ }.merge(options)
10
+
11
+ @app = app
12
+ @aggregate = ::Aggregate.new
13
+
14
+ yield self if block_given?
15
+ end
16
+
17
+ def call(env)
18
+ if env['PATH_INFO'] =~ AGGREGATE_PATH
19
+ resp = Rack::Response.new('', 200)
20
+ resp['Content-Type'] = 'text/plain'
21
+
22
+ resp.write "count:\t\t#{@aggregate.count}\n"
23
+ [:mean, :min, :max, :std_dev].each do |metric|
24
+ resp.write "#{metric}:\t\t%1.2fms\n" % (@aggregate.send(metric) || 0)
25
+ end
26
+
27
+ resp.write "Request histogram:\n"
28
+ resp.write @aggregate.to_s
29
+
30
+ return resp.finish
31
+ end
32
+
33
+ start = Time.now
34
+ @status, @headers, @response = @app.call(env)
35
+ @aggregate << (Time.now - start) * 1000
36
+
37
+ [@status, @headers, @response]
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,5 @@
1
+ module Rack
2
+ module Aggregate
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rack/aggregate/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "rack-aggregate"
7
+ s.version = Rack::Aggregate::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ilya Grigorik"]
10
+ s.email = ["ilya@igvita.com"]
11
+ s.homepage = "http://github.com/igrigorik/rack-aggregate"
12
+ s.summary = %q{Rack response-time statistics aggregator middleware}
13
+ s.description = s.summary
14
+
15
+ s.rubyforge_project = "rack-aggregate"
16
+
17
+ s.add_dependency "rack"
18
+ s.add_dependency "aggregate"
19
+ s.add_development_dependency "rspec"
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+ end
@@ -0,0 +1,38 @@
1
+ require 'helper'
2
+
3
+ describe Rack::Aggregate do
4
+ let(:app) { [200, {'Content-Type' => 'text/plain'}, 'Hello World'] }
5
+
6
+ describe 'middleware' do
7
+ it 'take a backend and returns a middleware component' do
8
+ Rack::Aggregate.new(app).should respond_to(:call)
9
+ end
10
+
11
+ it 'take an options Hash' do
12
+ lambda { Rack::Aggregate.new(app, {}) }.should_not raise_error(ArgumentError)
13
+ end
14
+ end
15
+
16
+ describe 'response' do
17
+ it 'should respond with 200 to requests to the aggregate endpoint' do
18
+ respond_with(200)
19
+ response = get('/aggregate')
20
+
21
+ response.status.should == 200
22
+ response.body.should match('Empty histogram')
23
+ end
24
+
25
+ it 'should collect aggregate performance statistics' do
26
+ respond_with(200)
27
+
28
+ 10.times { get('/some_endpoint') }
29
+ response = get('/aggregate')
30
+
31
+ response.status.should == 200
32
+ [:mean, :min, :max, :std_dev].each do |metric|
33
+ response.body.should match(/#{metric}:\s+\d\.\d+/)
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,39 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+
4
+ require 'rack/aggregate'
5
+
6
+ [ STDOUT, STDERR ].each { |io| io.sync = true }
7
+
8
+ def respond_with(status=200, headers={}, body=['Hello World'])
9
+ called = false
10
+ @app = lambda do |env|
11
+ called = true
12
+ response = Rack::Response.new(body, status, headers)
13
+ request = Rack::Request.new(env)
14
+
15
+ yield request, response if block_given?
16
+
17
+ response.finish
18
+ end
19
+
20
+ @app
21
+ end
22
+
23
+ def request(method, uri='/', opts={})
24
+ opts = {
25
+ 'rack.run_once' => true,
26
+ 'rack.errors' => @errors,
27
+ }.merge(opts)
28
+
29
+ @aggregate ||= Rack::Aggregate::Context.new(@app)
30
+ @request = Rack::MockRequest.new(@aggregate)
31
+
32
+ yield @aggregate if block_given?
33
+
34
+ @request.request(method.to_s.upcase, uri, opts)
35
+ end
36
+
37
+ def get(uri, env={}, &b)
38
+ request(:get, uri, env, &b)
39
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-aggregate
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Ilya Grigorik
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-01-09 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rack
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: aggregate
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ type: :runtime
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ prerelease: false
49
+ requirement: &id003 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 0
56
+ version: "0"
57
+ type: :development
58
+ version_requirements: *id003
59
+ description: Rack response-time statistics aggregator middleware
60
+ email:
61
+ - ilya@igvita.com
62
+ executables: []
63
+
64
+ extensions: []
65
+
66
+ extra_rdoc_files: []
67
+
68
+ files:
69
+ - .gitignore
70
+ - .rspec
71
+ - Gemfile
72
+ - Gemfile.lock
73
+ - README.md
74
+ - Rakefile
75
+ - examples/rackapp.rb
76
+ - lib/rack/aggregate.rb
77
+ - lib/rack/aggregate/context.rb
78
+ - lib/rack/aggregate/version.rb
79
+ - rack-aggregate.gemspec
80
+ - spec/context_spec.rb
81
+ - spec/helper.rb
82
+ has_rdoc: true
83
+ homepage: http://github.com/igrigorik/rack-aggregate
84
+ licenses: []
85
+
86
+ post_install_message:
87
+ rdoc_options: []
88
+
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ segments:
105
+ - 0
106
+ version: "0"
107
+ requirements: []
108
+
109
+ rubyforge_project: rack-aggregate
110
+ rubygems_version: 1.3.7
111
+ signing_key:
112
+ specification_version: 3
113
+ summary: Rack response-time statistics aggregator middleware
114
+ test_files:
115
+ - spec/context_spec.rb
116
+ - spec/helper.rb