ruby-metrics 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +5 -1
- data/README.md +49 -7
- data/examples/counter.rb +0 -1
- data/examples/gauge.rb +0 -1
- data/examples/histogram.rb +2 -2
- data/examples/integration/rack_endpoint.ru +24 -0
- data/examples/integration/rack_middleware.ru +21 -0
- data/examples/integration/webrick.rb +17 -0
- data/examples/meter.rb +4 -5
- data/examples/timer.rb +31 -0
- data/lib/ruby-metrics/agent.rb +4 -38
- data/lib/ruby-metrics/instruments.rb +29 -16
- data/lib/ruby-metrics/instruments/counter.rb +2 -0
- data/lib/ruby-metrics/instruments/gauge.rb +2 -0
- data/lib/ruby-metrics/instruments/histogram.rb +5 -2
- data/lib/ruby-metrics/instruments/meter.rb +13 -24
- data/lib/ruby-metrics/instruments/timer.rb +27 -5
- data/lib/ruby-metrics/integration.rb +12 -0
- data/lib/ruby-metrics/integration/rack_endpoint.rb +33 -0
- data/lib/ruby-metrics/integration/rack_middleware.rb +82 -0
- data/lib/ruby-metrics/integration/webrick.rb +47 -0
- data/lib/ruby-metrics/version.rb +1 -1
- data/ruby-metrics.gemspec +1 -0
- data/spec/agent_spec.rb +1 -13
- data/spec/instruments/timer_spec.rb +10 -6
- data/spec/instruments_spec.rb +15 -1
- data/spec/integration/rack_endpoint_spec.rb +60 -0
- data/spec/integration/rack_middleware_spec.rb +129 -0
- data/spec/integration/webrick_spec.rb +18 -0
- data/spec/spec_helper.rb +3 -0
- metadata +32 -5
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ruby-metrics (0.
|
4
|
+
ruby-metrics (0.8.0)
|
5
5
|
json
|
6
6
|
quantity
|
7
7
|
|
@@ -11,6 +11,9 @@ GEM
|
|
11
11
|
diff-lcs (1.1.2)
|
12
12
|
json (1.5.1)
|
13
13
|
quantity (0.1.1)
|
14
|
+
rack (1.2.2)
|
15
|
+
rack-test (0.5.7)
|
16
|
+
rack (>= 1.0)
|
14
17
|
rspec (2.5.0)
|
15
18
|
rspec-core (~> 2.5.0)
|
16
19
|
rspec-expectations (~> 2.5.0)
|
@@ -27,6 +30,7 @@ PLATFORMS
|
|
27
30
|
ruby
|
28
31
|
|
29
32
|
DEPENDENCIES
|
33
|
+
rack-test
|
30
34
|
rspec
|
31
35
|
ruby-metrics!
|
32
36
|
simplecov (>= 0.3.8)
|
data/README.md
CHANGED
@@ -16,26 +16,68 @@ Right now, I have:
|
|
16
16
|
* Gauges
|
17
17
|
* Histograms w/ uniform sampling
|
18
18
|
* Histograms w/ exponentially decaying sampling
|
19
|
-
|
20
|
-
Upcoming:
|
21
|
-
|
22
|
-
* Timers
|
19
|
+
* Timers
|
23
20
|
|
24
21
|
## Getting Started
|
25
22
|
|
26
23
|
The goal of ruby-metrics is to get up and running quickly. You start an agent, register some instruments, and they're exported over HTTP via JSON. For example, getting started with a counter would look like this:
|
27
24
|
|
28
25
|
@metrics = Metrics::Agent.new
|
29
|
-
@metrics.start
|
30
26
|
|
31
27
|
counter = @metrics.counter :my_counter
|
32
28
|
counter.incr
|
33
29
|
counter.incr
|
34
30
|
|
35
|
-
|
31
|
+
puts @metrics.to_json
|
32
|
+
#=> {"my_counter":"2"}
|
33
|
+
|
34
|
+
|
35
|
+
## Integration
|
36
|
+
|
37
|
+
Integrating ruby-metrics into existing applications is entirely up to your needs. Provided options include:
|
38
|
+
|
39
|
+
* Embedded WEBrick listener:
|
40
|
+
|
41
|
+
This runs a background thread and enables HTTP access to a local port (8001 by default) for a JSON representation of the current metrics.
|
42
|
+
|
43
|
+
``` ruby
|
44
|
+
require 'ruby-metrics/integration/webrick'
|
45
|
+
@agent = Metrics::Agent.new
|
46
|
+
@agent.start(:port => 8081)
|
47
|
+
```
|
48
|
+
|
49
|
+
* Rack Middleware:
|
50
|
+
|
51
|
+
This will add metrics such as `requests` (a timer) as well as counters for each class of HTTP status code (1xx, 2xx, etc). Also counts uncaught exceptions before reraising.
|
52
|
+
Provides a configurable path option (`:show`) to trigger the return of the metrics (as JSON) when the request path matches exactly (a string), as a regular expression, or as any object that responds to `call` for custom logic (passed the whole `env`).
|
53
|
+
|
54
|
+
``` ruby
|
55
|
+
require 'ruby-metrics'
|
56
|
+
@agent = Metrics::Agent.new
|
57
|
+
|
58
|
+
use Metrics::Integration::Rack::Middleware, :agent => @agent, :show => '/stats'
|
59
|
+
|
60
|
+
run app
|
61
|
+
```
|
62
|
+
|
63
|
+
* Rack Endpoint:
|
64
|
+
|
65
|
+
Use this to expose an endpoint for external consumption for your metrics.
|
66
|
+
Works best when used with a URLMap or mounted in addition to other routes, like Rails' `mount` route matcher.
|
67
|
+
|
68
|
+
``` ruby
|
69
|
+
require 'ruby-metrics'
|
70
|
+
@agent = Metrics::Agent.new
|
71
|
+
|
72
|
+
run Metrics::Integration::Rack::Endpoint.new(:agent => @agent)
|
73
|
+
```
|
36
74
|
|
37
|
-
|
75
|
+
or
|
38
76
|
|
77
|
+
``` ruby
|
78
|
+
# in config/router.rb
|
79
|
+
mount Metrics::Integration::Rack::Endpoint.new(:agent => @agent)
|
80
|
+
```
|
39
81
|
|
40
82
|
[metrics]: https://github.com/codahale/metrics
|
41
83
|
|
data/examples/counter.rb
CHANGED
data/examples/gauge.rb
CHANGED
data/examples/histogram.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require '../lib/ruby-metrics'
|
2
|
+
require '../lib/ruby-metrics/integration/webrick'
|
3
3
|
|
4
4
|
@metrics = Metrics::Agent.new
|
5
5
|
@metrics.start
|
@@ -16,7 +16,7 @@ end
|
|
16
16
|
step = 0
|
17
17
|
|
18
18
|
# This is here so that we will run indefinitely so you can hit the
|
19
|
-
# status page on localhost:8001/
|
19
|
+
# status page on localhost:8001/stats
|
20
20
|
loop do
|
21
21
|
sleep 1
|
22
22
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
# Ran with:
|
4
|
+
# rackup -p 8000 ./examples/integration/rack_endpoint.ru
|
5
|
+
#
|
6
|
+
# Make requests to: http://localhost:8000/
|
7
|
+
# See stats at : http://localhost:8000/stats
|
8
|
+
|
9
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ruby-metrics')
|
10
|
+
@agent = Metrics::Agent.new
|
11
|
+
|
12
|
+
counter = @agent.counter(:my_counter)
|
13
|
+
|
14
|
+
app = proc do |env|
|
15
|
+
counter.incr
|
16
|
+
[200, {'Content-Type' => 'text/plain'}, ["Counted!"]]
|
17
|
+
end
|
18
|
+
|
19
|
+
map = Rack::URLMap.new({
|
20
|
+
'/stats' => Metrics::Integration::Rack::Endpoint.new(:agent => @app),
|
21
|
+
'/' => app
|
22
|
+
})
|
23
|
+
|
24
|
+
run map
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
# Ran with:
|
4
|
+
# rackup -p 8000 ./examples/integration/rack_middleware.ru
|
5
|
+
#
|
6
|
+
# Make requests to: http://localhost:8000/
|
7
|
+
# See stats at : http://localhost:8000/stats
|
8
|
+
|
9
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ruby-metrics')
|
10
|
+
@agent = Metrics::Agent.new
|
11
|
+
|
12
|
+
counter = @agent.counter(:my_counter)
|
13
|
+
|
14
|
+
use Metrics::Integration::Rack::Middleware, :agent => @agent
|
15
|
+
|
16
|
+
app = proc do |env|
|
17
|
+
counter.incr
|
18
|
+
[200, {'Content-Type' => 'text/plain'}, ["Counted!"]]
|
19
|
+
end
|
20
|
+
|
21
|
+
run app
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib', 'ruby-metrics'))
|
3
|
+
|
4
|
+
# Run with:
|
5
|
+
# ruby examples/integration/webrick.rb
|
6
|
+
|
7
|
+
@agent = Metrics::Agent.new
|
8
|
+
|
9
|
+
counter = @agent.counter(:my_counter)
|
10
|
+
counter.incr
|
11
|
+
counter.incr
|
12
|
+
|
13
|
+
Metrics::Integration::WEBrick.start(:port => 8001,
|
14
|
+
:agent => @agent)
|
15
|
+
|
16
|
+
sleep
|
17
|
+
# Now navigate to: http://localhost:8001/stats
|
data/examples/meter.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require '../lib/ruby-metrics'
|
2
|
+
require '../lib/ruby-metrics/integration/webrick'
|
3
3
|
|
4
|
-
|
5
|
-
@metrics
|
6
|
-
@metrics.start
|
4
|
+
@metrics = Metrics::Agent.new
|
5
|
+
@metrics.start :port => 8081 # optional
|
7
6
|
|
8
7
|
timer = @metrics.meter :my_meter
|
9
8
|
timer.mark(500)
|
@@ -11,7 +10,7 @@ timer.mark(500)
|
|
11
10
|
step = 0
|
12
11
|
|
13
12
|
# This is here so that we will run indefinitely so you can hit the
|
14
|
-
# status page on localhost:8081/
|
13
|
+
# status page on localhost:8081/stats
|
15
14
|
loop do
|
16
15
|
sleep 1
|
17
16
|
|
data/examples/timer.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require '../lib/ruby-metrics/integration/webrick'
|
3
|
+
|
4
|
+
@metrics = Metrics::Agent.new
|
5
|
+
@metrics.start :port => 8081 # optional
|
6
|
+
|
7
|
+
timer = @metrics.timer :my_timer
|
8
|
+
timer.update(500, :milliseconds)
|
9
|
+
timer.update(5, :seconds)
|
10
|
+
timer.update(4242, :nanoseconds)
|
11
|
+
|
12
|
+
msec_timer = @metrics.timer :msec_timer, {:duration_unit => :microseconds, :rate_unit => :seconds}
|
13
|
+
|
14
|
+
step = 0
|
15
|
+
|
16
|
+
# This is here so that we will run indefinitely so you can hit the
|
17
|
+
# status page on localhost:8081/stats
|
18
|
+
loop do
|
19
|
+
sleep 1
|
20
|
+
|
21
|
+
modifier = rand(200).to_i
|
22
|
+
step += 1
|
23
|
+
|
24
|
+
if (step % 2)
|
25
|
+
modifier *= -1
|
26
|
+
end
|
27
|
+
|
28
|
+
timer.update(500 + modifier, :microseconds)
|
29
|
+
msec_timer.update(500 + modifier, :microseconds)
|
30
|
+
|
31
|
+
end
|
data/lib/ruby-metrics/agent.rb
CHANGED
@@ -1,26 +1,6 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'logging')
|
2
2
|
require File.join(File.dirname(__FILE__), 'instruments')
|
3
|
-
require '
|
4
|
-
|
5
|
-
class Status < WEBrick::HTTPServlet::AbstractServlet
|
6
|
-
|
7
|
-
def initialize(server, instruments)
|
8
|
-
@instruments = instruments
|
9
|
-
end
|
10
|
-
|
11
|
-
def do_GET(request, response)
|
12
|
-
status, content_type, body = do_stuff_with(request)
|
13
|
-
|
14
|
-
response.status = status
|
15
|
-
response['Content-Type'] = content_type
|
16
|
-
response.body = body
|
17
|
-
end
|
18
|
-
|
19
|
-
def do_stuff_with(request)
|
20
|
-
return 200, "text/plain", @instruments.to_json
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
3
|
+
require File.join(File.dirname(__FILE__), 'integration')
|
24
4
|
|
25
5
|
module Metrics
|
26
6
|
class Agent
|
@@ -29,28 +9,14 @@ module Metrics
|
|
29
9
|
|
30
10
|
attr_reader :instruments
|
31
11
|
|
32
|
-
def initialize
|
12
|
+
def initialize
|
33
13
|
logger.debug "Initializing Metrics..."
|
34
14
|
@instruments = Metrics::Instruments
|
35
|
-
@port = port
|
36
15
|
end
|
37
16
|
|
38
|
-
def
|
39
|
-
|
17
|
+
def to_json
|
18
|
+
@instruments.to_json
|
40
19
|
end
|
41
20
|
|
42
|
-
protected
|
43
|
-
def start_daemon_thread(connection_options = {})
|
44
|
-
logger.debug "Creating Metrics daemon thread."
|
45
|
-
@daemon_thread = Thread.new do
|
46
|
-
begin
|
47
|
-
server = WEBrick::HTTPServer.new ({:Port => @port})
|
48
|
-
server.mount "/status", Status, @instruments
|
49
|
-
server.start
|
50
|
-
rescue Exception => e
|
51
|
-
logger.error "Error in worker thread: #{e.class.name}: #{e}\n #{e.backtrace.join("\n ")}"
|
52
|
-
end # begin
|
53
|
-
end # thread new
|
54
|
-
end
|
55
21
|
end
|
56
22
|
end
|
@@ -4,27 +4,24 @@ require File.join(File.dirname(__FILE__), 'statistics', 'sample')
|
|
4
4
|
require File.join(File.dirname(__FILE__), 'statistics', 'uniform_sample')
|
5
5
|
require File.join(File.dirname(__FILE__), 'statistics', 'exponential_sample')
|
6
6
|
|
7
|
-
require File.join(File.dirname(__FILE__), 'instruments', 'base')
|
8
|
-
require File.join(File.dirname(__FILE__), 'instruments', 'counter')
|
9
|
-
require File.join(File.dirname(__FILE__), 'instruments', 'meter')
|
10
|
-
require File.join(File.dirname(__FILE__), 'instruments', 'gauge')
|
11
|
-
require File.join(File.dirname(__FILE__), 'instruments', 'histogram')
|
12
|
-
require File.join(File.dirname(__FILE__), 'instruments', 'timer')
|
13
|
-
|
14
|
-
|
15
7
|
require 'json'
|
16
8
|
|
17
9
|
module Metrics
|
18
10
|
module Instruments
|
19
11
|
@instruments = {}
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
12
|
+
@types = {}
|
13
|
+
|
14
|
+
def self.register_instrument(type, klass)
|
15
|
+
@types[type] = klass
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.registered_instruments
|
19
|
+
@types
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.register_with_options(type, name, options = {})
|
23
|
+
@instruments[name] = @types[type].new(options)
|
24
|
+
end
|
28
25
|
|
29
26
|
def self.register(type, name, &block)
|
30
27
|
@instruments[name] = @types[type].new(&block)
|
@@ -48,6 +45,10 @@ module Metrics
|
|
48
45
|
Metrics::Instruments.register(type, name, &block)
|
49
46
|
end
|
50
47
|
|
48
|
+
def register_with_options(type, name, options)
|
49
|
+
Metrics::Instruments.register_with_options(type, name, options)
|
50
|
+
end
|
51
|
+
|
51
52
|
def counter(name)
|
52
53
|
register(:counter, name)
|
53
54
|
end
|
@@ -60,6 +61,10 @@ module Metrics
|
|
60
61
|
register(:gauge, name, &block)
|
61
62
|
end
|
62
63
|
|
64
|
+
def timer(name, options = {})
|
65
|
+
register_with_options(:timer, name, options)
|
66
|
+
end
|
67
|
+
|
63
68
|
def uniform_histogram(name)
|
64
69
|
register(:uniform_histogram, name)
|
65
70
|
end
|
@@ -74,3 +79,11 @@ module Metrics
|
|
74
79
|
|
75
80
|
end
|
76
81
|
end
|
82
|
+
|
83
|
+
require File.join(File.dirname(__FILE__), 'instruments', 'base')
|
84
|
+
require File.join(File.dirname(__FILE__), 'instruments', 'counter')
|
85
|
+
require File.join(File.dirname(__FILE__), 'instruments', 'meter')
|
86
|
+
require File.join(File.dirname(__FILE__), 'instruments', 'gauge')
|
87
|
+
require File.join(File.dirname(__FILE__), 'instruments', 'histogram')
|
88
|
+
require File.join(File.dirname(__FILE__), 'instruments', 'timer')
|
89
|
+
|
@@ -166,13 +166,16 @@ module Metrics
|
|
166
166
|
super(:exponential)
|
167
167
|
end
|
168
168
|
end
|
169
|
-
|
169
|
+
|
170
|
+
register_instrument(:exponential_histogram, ExponentialHistogram)
|
171
|
+
|
170
172
|
class UniformHistogram < Histogram
|
171
173
|
def initialize
|
172
174
|
super(:uniform)
|
173
175
|
end
|
174
176
|
end
|
175
|
-
|
177
|
+
|
178
|
+
register_instrument(:uniform_histogram, UniformHistogram)
|
176
179
|
end
|
177
180
|
|
178
181
|
end
|
@@ -1,6 +1,10 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'time_units')
|
2
|
+
|
1
3
|
module Metrics
|
2
4
|
module Instruments
|
3
5
|
class Meter < Base
|
6
|
+
include Metrics::TimeConversion
|
7
|
+
|
4
8
|
# From http://www.teamquest.com/pdfs/whitepaper/ldavg2.pdf
|
5
9
|
INTERVAL = 5.0
|
6
10
|
INTERVAL_IN_NS = 5000000000.0
|
@@ -39,8 +43,8 @@ module Metrics
|
|
39
43
|
end
|
40
44
|
|
41
45
|
def calc_rate(rate, factor, count)
|
42
|
-
rate = rate + (factor * (count - rate))
|
43
|
-
rate
|
46
|
+
rate = rate.to_f + (factor.to_f * (count.to_f - rate.to_f))
|
47
|
+
rate.to_f
|
44
48
|
end
|
45
49
|
|
46
50
|
def tick
|
@@ -59,24 +63,24 @@ module Metrics
|
|
59
63
|
end
|
60
64
|
|
61
65
|
def one_minute_rate(rate_unit = :seconds)
|
62
|
-
|
66
|
+
convert_to_ns @one_minute_rate, rate_unit
|
63
67
|
end
|
64
68
|
|
65
69
|
def five_minute_rate(rate_unit = :seconds)
|
66
|
-
|
70
|
+
convert_to_ns @five_minute_rate, rate_unit
|
67
71
|
end
|
68
72
|
|
69
73
|
def fifteen_minute_rate(rate_unit = :seconds)
|
70
|
-
|
74
|
+
convert_to_ns @fifteen_minute_rate, rate_unit
|
71
75
|
end
|
72
76
|
|
73
|
-
def mean_rate(rate_unit = seconds)
|
77
|
+
def mean_rate(rate_unit = :seconds)
|
74
78
|
count = @count
|
75
79
|
if count == 0
|
76
80
|
return 0.0;
|
77
81
|
else
|
78
82
|
elapsed = Time.now.to_f - @start_time.to_f
|
79
|
-
|
83
|
+
convert_to_ns (count.to_f / elapsed.to_f), rate_unit
|
80
84
|
end
|
81
85
|
end
|
82
86
|
|
@@ -88,23 +92,8 @@ module Metrics
|
|
88
92
|
}.to_json
|
89
93
|
end
|
90
94
|
|
91
|
-
private
|
92
|
-
def scale_to_ns(value, unit)
|
93
|
-
mult = case unit
|
94
|
-
when :seconds
|
95
|
-
Seconds.to_nsec
|
96
|
-
when :minutes
|
97
|
-
Minutes.to_nsec
|
98
|
-
when :hours
|
99
|
-
Hours.to_nsec
|
100
|
-
when :days
|
101
|
-
Days.to_nsec
|
102
|
-
when :weeks
|
103
|
-
Weeks.to_nsec
|
104
|
-
end
|
105
|
-
|
106
|
-
value * mult
|
107
|
-
end
|
108
95
|
end
|
96
|
+
|
97
|
+
register_instrument(:meter, Meter)
|
109
98
|
end
|
110
99
|
end
|
@@ -7,12 +7,12 @@ module Metrics
|
|
7
7
|
|
8
8
|
attr_reader :duration_unit, :rate_unit
|
9
9
|
|
10
|
-
def initialize(
|
10
|
+
def initialize(options = {})
|
11
11
|
@meter = Meter.new
|
12
12
|
@histogram = ExponentialHistogram.new
|
13
13
|
|
14
|
-
@duration_unit = duration_unit
|
15
|
-
@rate_unit = rate_unit
|
14
|
+
@duration_unit = options[:duration_unit] || :seconds
|
15
|
+
@rate_unit = options[:rate_unit] || :seconds
|
16
16
|
|
17
17
|
clear
|
18
18
|
end
|
@@ -30,7 +30,8 @@ module Metrics
|
|
30
30
|
start_time = Time.now.to_f
|
31
31
|
result = block.call
|
32
32
|
time_diff = Time.now.to_f - start_time
|
33
|
-
|
33
|
+
time_in_ns = convert_to_ns time_diff, :seconds
|
34
|
+
update_timer(time_in_ns)
|
34
35
|
result
|
35
36
|
end
|
36
37
|
|
@@ -97,12 +98,33 @@ module Metrics
|
|
97
98
|
end
|
98
99
|
end
|
99
100
|
|
101
|
+
def to_s
|
102
|
+
{
|
103
|
+
:count => self.count,
|
104
|
+
:rates => {
|
105
|
+
:one_minute_rate => self.one_minute_rate,
|
106
|
+
:five_minute_rate => self.five_minute_rate,
|
107
|
+
:fifteen_minute_rate => self.fifteen_minute_rate,
|
108
|
+
:unit => @rate_unit
|
109
|
+
},
|
110
|
+
:durations => {
|
111
|
+
:min => self.min,
|
112
|
+
:max => self.max,
|
113
|
+
:mean => self.mean,
|
114
|
+
:percentiles => self.quantiles([0.25, 0.50, 0.75, 0.95, 0.97, 0.98, 0.99]),
|
115
|
+
:unit => @duration_unit
|
116
|
+
}
|
117
|
+
}.to_json
|
118
|
+
end
|
119
|
+
|
100
120
|
private
|
101
121
|
def scale_duration_to_ns(value, unit)
|
102
|
-
value / convert_to_ns(1, unit)
|
122
|
+
value.to_f / convert_to_ns(1, unit).to_f
|
103
123
|
end
|
104
124
|
|
105
125
|
end
|
126
|
+
|
127
|
+
register_instrument(:timer, Timer)
|
106
128
|
end
|
107
129
|
end
|
108
130
|
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Metrics
|
2
|
+
module Integration
|
3
|
+
|
4
|
+
autoload :WEBrick, File.join(File.dirname(__FILE__), 'integration', 'webrick')
|
5
|
+
|
6
|
+
module Rack
|
7
|
+
autoload :Middleware, File.join(File.dirname(__FILE__), 'integration', 'rack_middleware')
|
8
|
+
autoload :Endpoint, File.join(File.dirname(__FILE__), 'integration', 'rack_endpoint')
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Provides:
|
2
|
+
# * configurable agent
|
3
|
+
# * endpoint for accessing metrics JSON
|
4
|
+
#
|
5
|
+
module Metrics
|
6
|
+
module Integration
|
7
|
+
module Rack
|
8
|
+
class Endpoint
|
9
|
+
|
10
|
+
attr_accessor :app, :options, :agent,
|
11
|
+
# integration metrics
|
12
|
+
:requests, :uncaught_exceptions,
|
13
|
+
:status_codes
|
14
|
+
|
15
|
+
def initialize(options ={})
|
16
|
+
@options = options
|
17
|
+
@agent = @options.delete(:agent) || Agent.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(_)
|
21
|
+
body = @agent.to_json
|
22
|
+
|
23
|
+
[ 200,
|
24
|
+
{ 'Content-Type' => 'application/json',
|
25
|
+
'Content-Length' => body.size.to_s },
|
26
|
+
[body]
|
27
|
+
]
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# Provides:
|
2
|
+
# * configurable agent
|
3
|
+
# * configurable endpoint for current metrics
|
4
|
+
# * strings == path_info
|
5
|
+
# * regexp =~ path_info
|
6
|
+
# * proc.call(env) #=> boolean
|
7
|
+
# * env['metrics.agent'] upstream
|
8
|
+
# * specific metrics by default
|
9
|
+
# * requests (timer)
|
10
|
+
# * uncaught_exceptions (counter)
|
11
|
+
# * response_1xx through response_5xx (counter)
|
12
|
+
#
|
13
|
+
module Metrics
|
14
|
+
module Integration
|
15
|
+
module Rack
|
16
|
+
class Middleware
|
17
|
+
|
18
|
+
attr_accessor :app, :options, :agent,
|
19
|
+
# integration metrics
|
20
|
+
:requests, :uncaught_exceptions,
|
21
|
+
:status_codes
|
22
|
+
|
23
|
+
def initialize(app, options ={})
|
24
|
+
@app = app
|
25
|
+
@options = {:show => "/stats"}.merge(options)
|
26
|
+
@agent = @options.delete(:agent) || Agent.new
|
27
|
+
|
28
|
+
# Integration Metrics
|
29
|
+
@requests = @agent.timer(:_requests)
|
30
|
+
@uncaught_exceptions = @agent.counter(:_uncaught_exceptions)
|
31
|
+
|
32
|
+
# HTTP Status Codes
|
33
|
+
@status_codes = {
|
34
|
+
1 => @agent.counter(:_status_1xx),
|
35
|
+
2 => @agent.counter(:_status_2xx),
|
36
|
+
3 => @agent.counter(:_status_3xx),
|
37
|
+
4 => @agent.counter(:_status_4xx),
|
38
|
+
5 => @agent.counter(:_status_5xx)
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def call(env)
|
43
|
+
return show(env) if show?(env)
|
44
|
+
|
45
|
+
env['metrics.agent'] = @agent
|
46
|
+
|
47
|
+
status, headers, body = self.requests.time{ @app.call(env) }
|
48
|
+
|
49
|
+
if status_counter = self.status_codes[status / 100]
|
50
|
+
status_counter.incr
|
51
|
+
end
|
52
|
+
|
53
|
+
[status, headers, body]
|
54
|
+
rescue Exception
|
55
|
+
# TODO: add "last_uncaught_exception" with string of error
|
56
|
+
self.uncaught_exceptions.incr
|
57
|
+
raise
|
58
|
+
end
|
59
|
+
|
60
|
+
def show?(env, test = self.options[:show])
|
61
|
+
case
|
62
|
+
when String === test; env['PATH_INFO'] == test
|
63
|
+
when Regexp === test; env['PATH_INFO'] =~ test
|
64
|
+
when test.respond_to?(:call); test.call(env)
|
65
|
+
else test
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def show(_)
|
70
|
+
body = @agent.to_json
|
71
|
+
|
72
|
+
[ 200,
|
73
|
+
{ 'Content-Type' => 'application/json',
|
74
|
+
'Content-Length' => body.size.to_s },
|
75
|
+
[body]
|
76
|
+
]
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'ruby-metrics')
|
2
|
+
|
3
|
+
require 'webrick'
|
4
|
+
|
5
|
+
module Metrics
|
6
|
+
|
7
|
+
class Agent
|
8
|
+
def start(options = {})
|
9
|
+
Integration::WEBrick.start(options.merge(:agent => self))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Integration
|
14
|
+
class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
|
15
|
+
include Logging
|
16
|
+
|
17
|
+
def self.start(options = {})
|
18
|
+
connection_options = {:Port => options.delete(:port) || options.delete(:Port) || 8001}
|
19
|
+
agent = options.delete(:agent) || Agent.new
|
20
|
+
|
21
|
+
logger.debug "Creating Metrics daemon thread."
|
22
|
+
@thread = Thread.new do
|
23
|
+
begin
|
24
|
+
server = ::WEBrick::HTTPServer.new(connection_options)
|
25
|
+
server.mount "/stats", self, agent
|
26
|
+
server.start
|
27
|
+
rescue Exception => e
|
28
|
+
logger.error "Error in thread: %s: %s\n\t%s" % [e.class.to_s,
|
29
|
+
e.message,
|
30
|
+
e.backtrace.join("\n\t")]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(server, agent)
|
36
|
+
@agent = agent
|
37
|
+
end
|
38
|
+
|
39
|
+
def do_GET(request, response)
|
40
|
+
response.status = 200
|
41
|
+
response['Content-Type'] = 'application/json'
|
42
|
+
response.body = @agent.to_json
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/ruby-metrics/version.rb
CHANGED
data/ruby-metrics.gemspec
CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
|
20
20
|
s.add_development_dependency "rspec"
|
21
21
|
s.add_development_dependency "simplecov", [">= 0.3.8"] #, :require => false
|
22
|
+
s.add_development_dependency "rack-test"
|
22
23
|
|
23
24
|
s.files = `git ls-files`.split("\n")
|
24
25
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/spec/agent_spec.rb
CHANGED
@@ -45,16 +45,4 @@ describe Metrics::Agent do
|
|
45
45
|
@agent.meter(:test_meter).should == @meter
|
46
46
|
end
|
47
47
|
|
48
|
-
|
49
|
-
Thread.stub!(:new).and_return do |block|
|
50
|
-
block.call
|
51
|
-
end
|
52
|
-
|
53
|
-
mock_server = mock(WEBrick::HTTPServer)
|
54
|
-
WEBrick::HTTPServer.should_receive(:new).and_return mock_server
|
55
|
-
mock_server.should_receive(:mount)
|
56
|
-
mock_server.should_receive(:start)
|
57
|
-
@agent.start
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
48
|
+
end
|
@@ -9,7 +9,7 @@ describe Metrics::Instruments::Timer do
|
|
9
9
|
|
10
10
|
context "An empty timer" do
|
11
11
|
before(:each) do
|
12
|
-
@timer = Metrics::Instruments::Timer.new
|
12
|
+
@timer = Metrics::Instruments::Timer.new
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should have a max of zero" do
|
@@ -56,7 +56,7 @@ describe Metrics::Instruments::Timer do
|
|
56
56
|
|
57
57
|
context "Timing some events" do
|
58
58
|
before(:each) do
|
59
|
-
@timer = Metrics::Instruments::Timer.new(:milliseconds, :seconds)
|
59
|
+
@timer = Metrics::Instruments::Timer.new({:duration_unit => :milliseconds, :rate_unit => :seconds})
|
60
60
|
@timer.update(10, :milliseconds)
|
61
61
|
@timer.update(20, :milliseconds)
|
62
62
|
@timer.update(20, :milliseconds)
|
@@ -91,23 +91,27 @@ describe Metrics::Instruments::Timer do
|
|
91
91
|
it "should contain the series added" do
|
92
92
|
@timer.values.sort.should == [10, 20, 20, 30, 40]
|
93
93
|
end
|
94
|
+
|
95
|
+
it "should accurately represent itself using JSON" do
|
96
|
+
@timer.to_s.should == "{\"count\":5,\"rates\":{\"one_minute_rate\":0.0,\"five_minute_rate\":0.0,\"fifteen_minute_rate\":0.0,\"unit\":\"seconds\"},\"durations\":{\"min\":10.0,\"max\":40.0,\"mean\":24.0,\"percentiles\":{\"0.25\":20.0,\"0.5\":20.0,\"0.75\":30.0,\"0.95\":38.0,\"0.97\":38.8,\"0.98\":39.2,\"0.99\":39.6},\"unit\":\"milliseconds\"}}"
|
97
|
+
end
|
94
98
|
end
|
95
99
|
|
96
100
|
context "Timing blocks of code" do
|
97
101
|
before(:each) do
|
98
|
-
@timer = Metrics::Instruments::Timer.new(:nanoseconds, :nanoseconds)
|
102
|
+
@timer = Metrics::Instruments::Timer.new({:duration_unit => :nanoseconds, :rate_unit => :nanoseconds})
|
99
103
|
end
|
100
104
|
|
101
105
|
it "should return the result of the block passed" do
|
102
106
|
result = @timer.time do
|
103
|
-
sleep(
|
107
|
+
sleep(0.25)
|
104
108
|
"narf"
|
105
109
|
end
|
106
110
|
|
107
111
|
result.should == "narf"
|
108
112
|
|
109
|
-
@timer.max.should >=
|
110
|
-
@timer.max.should <=
|
113
|
+
@timer.max.should >= 250000000
|
114
|
+
@timer.max.should <= 300000000
|
111
115
|
|
112
116
|
end
|
113
117
|
end
|
data/spec/instruments_spec.rb
CHANGED
@@ -58,5 +58,19 @@ describe Metrics::Instruments do
|
|
58
58
|
proc = Proc.new { "result" }
|
59
59
|
@instruments.register :gauge, :test_gauge, &proc
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
|
+
context '#register_instrument' do
|
63
|
+
it 'registers a new instrument' do
|
64
|
+
lambda { Metrics::Instruments.register_instrument(:test, String) }.should_not raise_error
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'returns the list of registered instruments' do
|
68
|
+
Metrics::Instruments.register_instrument(:str, String)
|
69
|
+
Metrics::Instruments.register_instrument(:int, Fixnum)
|
70
|
+
|
71
|
+
inst = Metrics::Instruments.registered_instruments
|
72
|
+
inst[:str].should == String
|
73
|
+
inst[:int].should == Fixnum
|
74
|
+
end
|
75
|
+
end
|
62
76
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "rack/test"
|
2
|
+
|
3
|
+
describe Metrics::Integration::Rack::Endpoint do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def app(options = {})
|
7
|
+
@app ||= Metrics::Integration::Rack::Endpoint.new(options)
|
8
|
+
end
|
9
|
+
def agent
|
10
|
+
app.agent
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should show stats" do
|
14
|
+
get "/"
|
15
|
+
last_response.should be_ok
|
16
|
+
last_response.body.should == agent.to_json
|
17
|
+
end
|
18
|
+
|
19
|
+
context "configuring" do
|
20
|
+
|
21
|
+
context "agent" do
|
22
|
+
it "should create an agent by default" do
|
23
|
+
app(:agent => nil)
|
24
|
+
agent.should be_a(Metrics::Agent)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should use an agent if provided" do
|
28
|
+
@agent = Metrics::Agent.new
|
29
|
+
app(:agent => @agent)
|
30
|
+
agent.should be_a(Metrics::Agent)
|
31
|
+
agent.should == @agent
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "show" do
|
36
|
+
it "should match a string to the PATH_INFO exactly" do
|
37
|
+
app(:show => "/foo")
|
38
|
+
get '/foo'
|
39
|
+
last_response.should be_ok
|
40
|
+
last_response.body.should == agent.to_json
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should match a regular expression to the PATH_INFO" do
|
44
|
+
app(:show => /bar/)
|
45
|
+
get '/foobarbaz'
|
46
|
+
last_response.should be_ok
|
47
|
+
last_response.body.should == agent.to_json
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should call `call` on the show option if it responds to it" do
|
51
|
+
app(:show => lambda{ |env| env['PATH_INFO'] == "/bing" })
|
52
|
+
get '/bing'
|
53
|
+
last_response.should be_ok
|
54
|
+
last_response.body.should == agent.to_json
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require "rack/test"
|
2
|
+
|
3
|
+
describe Metrics::Integration::Rack::Middleware do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def app(status = 200, body = ["Hello, World!"], headers = {})
|
7
|
+
@app ||= lambda{ |env| [status, {'Content-Type' => 'text/plain', 'Content-Length' => body.to_s.size.to_s}.merge(headers), body] }
|
8
|
+
end
|
9
|
+
alias_method :original_app, :app
|
10
|
+
|
11
|
+
describe "without integration" do
|
12
|
+
it "should work normally" do
|
13
|
+
get "/"
|
14
|
+
|
15
|
+
last_response.should be_ok
|
16
|
+
last_response.body.should == "Hello, World!"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "with integration" do
|
21
|
+
def app(options = {})
|
22
|
+
@integrated_app ||= Metrics::Integration::Rack::Middleware.new(original_app, options)
|
23
|
+
end
|
24
|
+
def agent
|
25
|
+
app.agent
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should work normally" do
|
29
|
+
get "/"
|
30
|
+
|
31
|
+
last_response.should be_ok
|
32
|
+
last_response.body.should == "Hello, World!"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should show stats for default endpoint" do
|
36
|
+
get "/stats"
|
37
|
+
last_response.should be_ok
|
38
|
+
last_response.body.should == agent.to_json
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should make 'metrics.agent' available to the upstream environment" do
|
42
|
+
@app = lambda do |env|
|
43
|
+
env['metrics.agent'].should be_a(Metrics::Agent)
|
44
|
+
env['metrics.agent'].should == agent
|
45
|
+
[200, {}, []]
|
46
|
+
end
|
47
|
+
|
48
|
+
get '/'
|
49
|
+
last_response.should be_ok
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "integration metrics" do
|
53
|
+
|
54
|
+
it "should count all requests" do
|
55
|
+
lambda do
|
56
|
+
get '/'
|
57
|
+
end.should change{ app.requests.count }.by(1)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should count uncaught exceptions" do
|
61
|
+
@app = lambda{ |env| raise }
|
62
|
+
lambda do
|
63
|
+
lambda do
|
64
|
+
get '/'
|
65
|
+
end.should raise_error
|
66
|
+
end.should change{ app.uncaught_exceptions.to_i }.by(1)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should time request length" do
|
70
|
+
length = 0.1
|
71
|
+
@app = lambda{ |env| sleep(length); [200, {}, ['']] }
|
72
|
+
get '/'
|
73
|
+
app.requests.mean.should be_within(length / 10).of(length)
|
74
|
+
end
|
75
|
+
|
76
|
+
[ 200, 304, 404, 500].each do |status|
|
77
|
+
it "should count #{status} HTTP status code as #{status / 100}xx" do
|
78
|
+
@app = lambda{ |env| [status, {}, []] }
|
79
|
+
lambda do
|
80
|
+
get '/'
|
81
|
+
end.should change{ app.status_codes[status / 100].to_i }.by(1)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
context "configuring" do
|
88
|
+
|
89
|
+
context "agent" do
|
90
|
+
it "should create an agent by default" do
|
91
|
+
app(:agent => nil)
|
92
|
+
agent.should be_a(Metrics::Agent)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should use an agent if provided" do
|
96
|
+
@agent = Metrics::Agent.new
|
97
|
+
app(:agent => @agent)
|
98
|
+
agent.should be_a(Metrics::Agent)
|
99
|
+
agent.should == @agent
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "show" do
|
104
|
+
it "should match a string to the PATH_INFO exactly" do
|
105
|
+
app(:show => "/foo")
|
106
|
+
get '/foo'
|
107
|
+
last_response.should be_ok
|
108
|
+
last_response.body.should == agent.to_json
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should match a regular expression to the PATH_INFO" do
|
112
|
+
app(:show => /bar/)
|
113
|
+
get '/foobarbaz'
|
114
|
+
last_response.should be_ok
|
115
|
+
last_response.body.should == agent.to_json
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should call `call` on the show option if it responds to it" do
|
119
|
+
app(:show => lambda{ |env| env['PATH_INFO'] == "/bing" })
|
120
|
+
get '/bing'
|
121
|
+
last_response.should be_ok
|
122
|
+
last_response.body.should == agent.to_json
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "rack/test"
|
2
|
+
|
3
|
+
describe Metrics::Integration::WEBrick do
|
4
|
+
|
5
|
+
it "should start the WEBrick thread" do
|
6
|
+
Thread.stub!(:new).and_return do |block|
|
7
|
+
block.call
|
8
|
+
end
|
9
|
+
|
10
|
+
mock_server = mock(WEBrick::HTTPServer)
|
11
|
+
WEBrick::HTTPServer.should_receive(:new).and_return mock_server
|
12
|
+
mock_server.should_receive(:mount)
|
13
|
+
mock_server.should_receive(:start)
|
14
|
+
|
15
|
+
Metrics::Integration::WEBrick.start
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 8
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.8.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- John Ewart
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-04-
|
17
|
+
date: 2011-04-26 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -71,6 +71,19 @@ dependencies:
|
|
71
71
|
type: :development
|
72
72
|
prerelease: false
|
73
73
|
version_requirements: *id004
|
74
|
+
- !ruby/object:Gem::Dependency
|
75
|
+
name: rack-test
|
76
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
type: :development
|
85
|
+
prerelease: false
|
86
|
+
version_requirements: *id005
|
74
87
|
description: A Ruby implementation of metrics inspired by @coda's JVM metrics for those of us in Ruby land
|
75
88
|
email:
|
76
89
|
- john@johnewart.net
|
@@ -92,7 +105,11 @@ files:
|
|
92
105
|
- examples/counter.rb
|
93
106
|
- examples/gauge.rb
|
94
107
|
- examples/histogram.rb
|
108
|
+
- examples/integration/rack_endpoint.ru
|
109
|
+
- examples/integration/rack_middleware.ru
|
110
|
+
- examples/integration/webrick.rb
|
95
111
|
- examples/meter.rb
|
112
|
+
- examples/timer.rb
|
96
113
|
- lib/ruby-metrics.rb
|
97
114
|
- lib/ruby-metrics/agent.rb
|
98
115
|
- lib/ruby-metrics/instruments.rb
|
@@ -102,6 +119,10 @@ files:
|
|
102
119
|
- lib/ruby-metrics/instruments/histogram.rb
|
103
120
|
- lib/ruby-metrics/instruments/meter.rb
|
104
121
|
- lib/ruby-metrics/instruments/timer.rb
|
122
|
+
- lib/ruby-metrics/integration.rb
|
123
|
+
- lib/ruby-metrics/integration/rack_endpoint.rb
|
124
|
+
- lib/ruby-metrics/integration/rack_middleware.rb
|
125
|
+
- lib/ruby-metrics/integration/webrick.rb
|
105
126
|
- lib/ruby-metrics/logging.rb
|
106
127
|
- lib/ruby-metrics/statistics/exponential_sample.rb
|
107
128
|
- lib/ruby-metrics/statistics/sample.rb
|
@@ -117,6 +138,9 @@ files:
|
|
117
138
|
- spec/instruments/meter_spec.rb
|
118
139
|
- spec/instruments/timer_spec.rb
|
119
140
|
- spec/instruments_spec.rb
|
141
|
+
- spec/integration/rack_endpoint_spec.rb
|
142
|
+
- spec/integration/rack_middleware_spec.rb
|
143
|
+
- spec/integration/webrick_spec.rb
|
120
144
|
- spec/spec_helper.rb
|
121
145
|
- spec/statistics/exponential_sample_spec.rb
|
122
146
|
- spec/statistics/sample_spec.rb
|
@@ -136,7 +160,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
136
160
|
requirements:
|
137
161
|
- - ">="
|
138
162
|
- !ruby/object:Gem::Version
|
139
|
-
hash:
|
163
|
+
hash: 4032034234176091931
|
140
164
|
segments:
|
141
165
|
- 0
|
142
166
|
version: "0"
|
@@ -145,7 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
169
|
requirements:
|
146
170
|
- - ">="
|
147
171
|
- !ruby/object:Gem::Version
|
148
|
-
hash:
|
172
|
+
hash: 4032034234176091931
|
149
173
|
segments:
|
150
174
|
- 0
|
151
175
|
version: "0"
|
@@ -165,6 +189,9 @@ test_files:
|
|
165
189
|
- spec/instruments/meter_spec.rb
|
166
190
|
- spec/instruments/timer_spec.rb
|
167
191
|
- spec/instruments_spec.rb
|
192
|
+
- spec/integration/rack_endpoint_spec.rb
|
193
|
+
- spec/integration/rack_middleware_spec.rb
|
194
|
+
- spec/integration/webrick_spec.rb
|
168
195
|
- spec/spec_helper.rb
|
169
196
|
- spec/statistics/exponential_sample_spec.rb
|
170
197
|
- spec/statistics/sample_spec.rb
|