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.
- data/.gitignore +3 -0
- data/.rspec +0 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +30 -0
- data/README.md +53 -0
- data/Rakefile +2 -0
- data/examples/rackapp.rb +23 -0
- data/lib/rack/aggregate.rb +12 -0
- data/lib/rack/aggregate/context.rb +41 -0
- data/lib/rack/aggregate/version.rb +5 -0
- data/rack-aggregate.gemspec +25 -0
- data/spec/context_spec.rb +38 -0
- data/spec/helper.rb +39 -0
- metadata +116 -0
data/.gitignore
ADDED
data/.rspec
ADDED
File without changes
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -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
|
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
data/examples/rackapp.rb
ADDED
@@ -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,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,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
|
data/spec/helper.rb
ADDED
@@ -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
|