ruby-metrics 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +32 -0
- data/LICENSE +21 -0
- data/README.md +44 -0
- data/Rakefile +19 -0
- data/autotest/discover.rb +3 -0
- data/examples/counter.rb +11 -0
- data/examples/gauge.rb +26 -0
- data/examples/histogram.rb +28 -0
- data/examples/meter.rb +25 -0
- data/lib/ruby-metrics.rb +15 -0
- data/lib/ruby-metrics/agent.rb +55 -0
- data/lib/ruby-metrics/instruments.rb +64 -0
- data/lib/ruby-metrics/instruments/base.rb +19 -0
- data/lib/ruby-metrics/instruments/counter.rb +33 -0
- data/lib/ruby-metrics/instruments/gauge.rb +20 -0
- data/lib/ruby-metrics/instruments/histogram.rb +155 -0
- data/lib/ruby-metrics/instruments/meter.rb +99 -0
- data/lib/ruby-metrics/logging.rb +21 -0
- data/lib/ruby-metrics/statistics/sample.rb +21 -0
- data/lib/ruby-metrics/statistics/uniform_sample.rb +38 -0
- data/lib/ruby-metrics/version.rb +3 -0
- data/ruby-metrics.gemspec +27 -0
- data/spec/agent_spec.rb +42 -0
- data/spec/instruments/base_spec.rb +16 -0
- data/spec/instruments/counter_spec.rb +72 -0
- data/spec/instruments/gauge_spec.rb +44 -0
- data/spec/instruments/histogram_spec.rb +89 -0
- data/spec/instruments/meter_spec.rb +80 -0
- data/spec/instruments_spec.rb +47 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/statistics/sample_spec.rb +22 -0
- data/spec/statistics/uniform_sample_spec.rb +59 -0
- metadata +163 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm ruby-1.9.2@metrics
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
ruby-metrics (0.5.0)
|
5
|
+
json
|
6
|
+
ruby-units
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
diff-lcs (1.1.2)
|
12
|
+
json (1.5.1)
|
13
|
+
rspec (2.5.0)
|
14
|
+
rspec-core (~> 2.5.0)
|
15
|
+
rspec-expectations (~> 2.5.0)
|
16
|
+
rspec-mocks (~> 2.5.0)
|
17
|
+
rspec-core (2.5.1)
|
18
|
+
rspec-expectations (2.5.0)
|
19
|
+
diff-lcs (~> 1.1.2)
|
20
|
+
rspec-mocks (2.5.0)
|
21
|
+
ruby-units (1.2.0)
|
22
|
+
simplecov (0.4.0)
|
23
|
+
simplecov-html (~> 0.4.0)
|
24
|
+
simplecov-html (0.4.3)
|
25
|
+
|
26
|
+
PLATFORMS
|
27
|
+
ruby
|
28
|
+
|
29
|
+
DEPENDENCIES
|
30
|
+
rspec
|
31
|
+
ruby-metrics!
|
32
|
+
simplecov (>= 0.3.8)
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2011 John Ewart
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
## What is this?
|
3
|
+
|
4
|
+
This is a Ruby version of performance metrics inspired by [metrics][metrics] developed by Coda Hale at Yammer. Currently this is under *heavy* development -- it needs Gem packaging, more features, validation of metrics, more functional testing, and a little better test coverage. Pull requests happily accepted, please include docs and tests where possible!
|
5
|
+
|
6
|
+
## What needs to be done?
|
7
|
+
|
8
|
+
Among other important things, this needs to be made more thread-safe. I'm currently looking at Mr. Nutter's ruby-atomic gem for making this less tedious but any suggestions are welcome!
|
9
|
+
|
10
|
+
## What's in this?
|
11
|
+
|
12
|
+
Right now, I have:
|
13
|
+
|
14
|
+
* Counters
|
15
|
+
* Meters
|
16
|
+
* Gauges
|
17
|
+
* Histograms w/ uniform sampling
|
18
|
+
|
19
|
+
Upcoming:
|
20
|
+
|
21
|
+
* Histograms w/ exponentially decaying sampling
|
22
|
+
* Timers
|
23
|
+
|
24
|
+
## Getting Started
|
25
|
+
|
26
|
+
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
|
+
|
28
|
+
@metrics = Metrics::Agent.new
|
29
|
+
@metrics.start
|
30
|
+
|
31
|
+
counter = @metrics.counter :my_counter
|
32
|
+
counter.incr
|
33
|
+
counter.incr
|
34
|
+
|
35
|
+
Then, hitting localhost:8081/status would yield:
|
36
|
+
|
37
|
+
{"my_counter":"2"}
|
38
|
+
|
39
|
+
|
40
|
+
[metrics]: https://github.com/codahale/metrics
|
41
|
+
|
42
|
+
## License
|
43
|
+
|
44
|
+
Copyright 2011 John Ewart <john@johnewart.net>. Released under the MIT license. See the file LICENSE for further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler"
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
|
6
|
+
begin
|
7
|
+
Bundler.setup(:default, :development)
|
8
|
+
rescue Bundler::BundlerError => e
|
9
|
+
$stderr.puts e.message
|
10
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
11
|
+
exit e.status_code
|
12
|
+
end
|
13
|
+
|
14
|
+
RSpec::Core::RakeTask.new do |t|
|
15
|
+
t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
|
16
|
+
t.pattern = 'spec/**/*_spec.rb'
|
17
|
+
end
|
18
|
+
|
19
|
+
task :default => :spec
|
data/examples/counter.rb
ADDED
data/examples/gauge.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require '../lib/ruby-metrics'
|
3
|
+
|
4
|
+
@metrics = Metrics::Agent.new
|
5
|
+
@metrics.start
|
6
|
+
|
7
|
+
hit_count = 42
|
8
|
+
http_requests = 53
|
9
|
+
|
10
|
+
gauge = @metrics.gauge :my_gauge do
|
11
|
+
{
|
12
|
+
:hit_count => hit_count,
|
13
|
+
:http_requests => http_requests
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
puts "Gauge: #{gauge.to_s}"
|
18
|
+
|
19
|
+
hit_count = 65
|
20
|
+
http_requests = 99
|
21
|
+
|
22
|
+
puts "Gauge: #{gauge.to_s}"
|
23
|
+
|
24
|
+
result = gauge.get
|
25
|
+
|
26
|
+
puts "Result: #{result}"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require '../lib/ruby-metrics'
|
3
|
+
|
4
|
+
@metrics = Metrics::Agent.new
|
5
|
+
@metrics.start
|
6
|
+
|
7
|
+
histogram = @metrics.histogram :my_histogram
|
8
|
+
data = %w(3.600 1.800 3.333 2.283 4.533 2.883 4.700 3.600 1.950 4.350 1.833 3.917 4.200 1.750 4.700 2.167 1.750 4.800 1.600 4.250 1.800 1.750 3.450 3.067 4.533 3.600 1.967 4.083 3.850 4.433 4.300 4.467 3.367 4.033 3.833 2.017 1.867 4.833 1.833 4.783 4.350 1.883 4.567 1.750 4.533 3.317 3.833 2.100 4.633 2.000 4.800 4.716 1.833 4.833 1.733 4.883 3.717 1.667 4.567 4.317 2.233 4.500 1.750 4.800 1.817 4.400 4.167 4.700 2.067 4.700 4.033 1.967 4.500 4.000 1.983 5.067 2.017 4.567 3.883 3.600 4.133 4.333 4.100 2.633 4.067 4.933 3.950 4.517 2.167 4.000 2.200 4.333 1.867 4.817 1.833 4.300 4.667 3.750 1.867 4.900 2.483 4.367 2.100 4.500 4.050 1.867 4.700 1.783 4.850 3.683 4.733 2.300 4.900 4.417 1.700 4.633 2.317 4.600 1.817 4.417 2.617 4.067 4.250 1.967 4.600 3.767 1.917 4.500 2.267 4.650 1.867 4.167 2.800 4.333 1.833 4.383 1.883 4.933 2.033 3.733 4.233 2.233 4.533 4.817 4.333 1.983 4.633 2.017 5.100 1.800 5.033 4.000 2.400 4.600 3.567 4.000 4.500 4.083 1.800 3.967 2.200 4.150 2.000 3.833 3.500 4.583 2.367 5.000 1.933 4.617 1.917 2.083 4.583 3.333 4.167 4.333 4.500 2.417 4.000 4.167 1.883 4.583 4.250 3.767 2.033 4.433 4.083 1.833 4.417 2.183 4.800 1.833 4.800 4.100 3.966 4.233 3.500 4.366 2.250 4.667 2.100 4.350 4.133 1.867 4.600 1.783 4.367 3.850 1.933 4.500 2.383 4.700 1.867 3.833 3.417 4.233 2.400 4.800 2.000 4.150 1.867 4.267 1.750 4.483 4.000 4.117 4.083 4.267 3.917 4.550 4.083 2.417 4.183 2.217 4.450 1.883 1.850 4.283 3.950 2.333 4.150 2.350 4.933 2.900 4.583 3.833 2.083 4.367 2.133 4.350 2.200 4.450 3.567 4.500 4.150 3.817 3.917 4.450 2.000 4.283 4.767 4.533 1.850 4.250 1.983 2.250 4.750 4.117 2.150 4.417 1.817 4.467)
|
9
|
+
data.each do |point|
|
10
|
+
histogram.update(point.to_f)
|
11
|
+
end
|
12
|
+
|
13
|
+
step = 0
|
14
|
+
|
15
|
+
# This is here so that we will run indefinitely so you can hit the
|
16
|
+
# status page on localhost:8001/status
|
17
|
+
loop do
|
18
|
+
sleep 1
|
19
|
+
|
20
|
+
modifier = rand(500).to_i / 100
|
21
|
+
step += 1
|
22
|
+
|
23
|
+
if (step % 2)
|
24
|
+
modifier *= -1
|
25
|
+
end
|
26
|
+
|
27
|
+
histogram.update((2 + modifier).to_f)
|
28
|
+
end
|
data/examples/meter.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require '../lib/ruby-metrics'
|
3
|
+
|
4
|
+
@metrics = Metrics::Agent.new
|
5
|
+
@metrics.start
|
6
|
+
|
7
|
+
timer = @metrics.meter :my_meter
|
8
|
+
timer.mark(500)
|
9
|
+
|
10
|
+
step = 0
|
11
|
+
|
12
|
+
# This is here so that we will run indefinitely so you can hit the
|
13
|
+
# status page on localhost:8001/status
|
14
|
+
loop do
|
15
|
+
sleep 1
|
16
|
+
|
17
|
+
modifier = rand(200).to_i
|
18
|
+
step += 1
|
19
|
+
|
20
|
+
if (step % 2)
|
21
|
+
modifier *= -1
|
22
|
+
end
|
23
|
+
|
24
|
+
timer.mark(500 + modifier)
|
25
|
+
end
|
data/lib/ruby-metrics.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'logging')
|
2
|
+
require File.join(File.dirname(__FILE__), 'instruments')
|
3
|
+
require 'webrick'
|
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
|
24
|
+
|
25
|
+
module Metrics
|
26
|
+
class Agent
|
27
|
+
include Logging
|
28
|
+
include Instruments::TypeMethods
|
29
|
+
|
30
|
+
attr_reader :instruments
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
logger.debug "Initializing Metrics..."
|
34
|
+
@instruments = Metrics::Instruments
|
35
|
+
end
|
36
|
+
|
37
|
+
def start
|
38
|
+
start_daemon_thread
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
def start_daemon_thread(connection_options = {})
|
43
|
+
logger.debug "Creating Metrics daemon thread."
|
44
|
+
@daemon_thread = Thread.new do
|
45
|
+
begin
|
46
|
+
server = WEBrick::HTTPServer.new ({:Port => 8001})
|
47
|
+
server.mount "/status", Status, @instruments
|
48
|
+
server.start
|
49
|
+
rescue Exception => e
|
50
|
+
logger.error "Error in worker thread: #{e.class.name}: #{e}\n #{e.backtrace.join("\n ")}"
|
51
|
+
end # begin
|
52
|
+
end # thread new
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'statistics', 'sample')
|
2
|
+
require File.join(File.dirname(__FILE__), 'statistics', 'uniform_sample')
|
3
|
+
|
4
|
+
require File.join(File.dirname(__FILE__), 'instruments', 'base')
|
5
|
+
require File.join(File.dirname(__FILE__), 'instruments', 'counter')
|
6
|
+
require File.join(File.dirname(__FILE__), 'instruments', 'meter')
|
7
|
+
require File.join(File.dirname(__FILE__), 'instruments', 'gauge')
|
8
|
+
require File.join(File.dirname(__FILE__), 'instruments', 'histogram')
|
9
|
+
|
10
|
+
|
11
|
+
require 'json'
|
12
|
+
|
13
|
+
module Metrics
|
14
|
+
module Instruments
|
15
|
+
@instruments = {}
|
16
|
+
|
17
|
+
@types = {
|
18
|
+
:counter => Counter,
|
19
|
+
:meter => Meter,
|
20
|
+
:gauge => Gauge,
|
21
|
+
:histogram => Histogram
|
22
|
+
}
|
23
|
+
|
24
|
+
def self.register(type, name, &block)
|
25
|
+
@instruments[name] = @types[type].new(&block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.unregister_all
|
29
|
+
@instruments = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.registered
|
33
|
+
@instruments
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.to_json
|
37
|
+
@instruments.to_json
|
38
|
+
end
|
39
|
+
|
40
|
+
module TypeMethods
|
41
|
+
|
42
|
+
def register(type, name, &block)
|
43
|
+
Metrics::Instruments.register(type, name, &block)
|
44
|
+
end
|
45
|
+
|
46
|
+
def counter(name)
|
47
|
+
register(:counter, name)
|
48
|
+
end
|
49
|
+
|
50
|
+
def meter(name)
|
51
|
+
register(:meter, name)
|
52
|
+
end
|
53
|
+
|
54
|
+
def gauge(name, &block)
|
55
|
+
register(:gauge, name, &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def histogram(name)
|
59
|
+
register(:histogram, name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Metrics
|
2
|
+
module Instruments
|
3
|
+
class Counter < Base
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@value = 0
|
7
|
+
end
|
8
|
+
|
9
|
+
def inc(value = 1)
|
10
|
+
@value += value
|
11
|
+
end
|
12
|
+
alias_method :incr, :inc
|
13
|
+
|
14
|
+
def dec(value = 1)
|
15
|
+
@value -= value
|
16
|
+
end
|
17
|
+
alias_method :decr, :dec
|
18
|
+
|
19
|
+
def clear
|
20
|
+
@value = 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_i
|
24
|
+
@value.to_i
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
@value.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Metrics
|
2
|
+
module Instruments
|
3
|
+
class Gauge < Base
|
4
|
+
|
5
|
+
def initialize(&block)
|
6
|
+
raise ArgumentError, "a block is required" unless block_given?
|
7
|
+
@block = block
|
8
|
+
end
|
9
|
+
|
10
|
+
def get
|
11
|
+
instance_exec(&@block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
get.to_json
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|