ruby-metrics 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,15 +1,16 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-metrics (0.6.0)
4
+ ruby-metrics (0.7.0)
5
5
  json
6
- ruby-units
6
+ quantity
7
7
 
8
8
  GEM
9
9
  remote: http://rubygems.org/
10
10
  specs:
11
11
  diff-lcs (1.1.2)
12
12
  json (1.5.1)
13
+ quantity (0.1.1)
13
14
  rspec (2.5.0)
14
15
  rspec-core (~> 2.5.0)
15
16
  rspec-expectations (~> 2.5.0)
@@ -18,7 +19,6 @@ GEM
18
19
  rspec-expectations (2.5.0)
19
20
  diff-lcs (~> 1.1.2)
20
21
  rspec-mocks (2.5.0)
21
- ruby-units (1.2.0)
22
22
  simplecov (0.4.0)
23
23
  simplecov-html (~> 0.4.0)
24
24
  simplecov-html (0.4.3)
@@ -145,6 +145,10 @@ module Metrics
145
145
  end
146
146
  end
147
147
 
148
+ def values
149
+ @sample.values
150
+ end
151
+
148
152
  def to_s
149
153
  {
150
154
  :min => self.min,
@@ -154,7 +158,7 @@ module Metrics
154
158
  :percentiles => self.quantiles([0.25, 0.50, 0.75, 0.95, 0.97, 0.98, 0.99])
155
159
  }.to_json
156
160
  end
157
-
161
+
158
162
  end
159
163
 
160
164
  class ExponentialHistogram < Histogram
@@ -1,5 +1,3 @@
1
- require 'ruby-units'
2
-
3
1
  module Metrics
4
2
  module Instruments
5
3
  class Meter < Base
@@ -10,31 +8,17 @@ module Metrics
10
8
  FIVE_MINUTE_FACTOR = 1 - Math.exp(-INTERVAL / (60.0 * 5.0))
11
9
  FIFTEEN_MINUTE_FACTOR = 1 - Math.exp(-INTERVAL / (60.0 * 15.0))
12
10
 
13
- attr_reader :counted, :uncounted
11
+ attr_reader :count
12
+ alias_method :counted, :count
14
13
 
15
14
  def initialize(options = {})
16
15
  @one_minute_rate = @five_minute_rate = @fifteen_minute_rate = 0.0
17
- @counted = @uncounted = 0
16
+ @count = 0
18
17
  @initialized = false
19
-
20
- if options[:interval]
21
- interval = options[:interval]
22
- else
23
- interval = "5 seconds"
24
- end
25
-
26
- if options[:rateunit]
27
- @rateunit = options[:rateunit]
28
- else
29
- @rateunit = interval
30
- end
31
-
32
- # HACK: this is here because ruby-units thinks 1s in ns is 999,999,999.9999999 not 1bn
33
- # TODO: either fix ruby-units, or remove it?
34
- @ratemultiplier = @rateunit.to("nanoseconds").scalar.ceil
35
-
18
+ @start_time = Time.now.to_f
19
+
36
20
  @timer_thread = Thread.new do
37
- sleep_time = interval.to("seconds").scalar
21
+ sleep_time = INTERVAL
38
22
  begin
39
23
  loop do
40
24
  self.tick
@@ -51,8 +35,7 @@ module Metrics
51
35
  end
52
36
 
53
37
  def mark(count = 1)
54
- @uncounted += count
55
- @counted += count
38
+ @count += count
56
39
  end
57
40
 
58
41
  def calc_rate(rate, factor, count)
@@ -61,7 +44,7 @@ module Metrics
61
44
  end
62
45
 
63
46
  def tick
64
- count = @uncounted.to_f / INTERVAL_IN_NS.to_f
47
+ count = @count.to_f / Seconds.to_nsec(INTERVAL).to_f
65
48
 
66
49
  if (@initialized)
67
50
  @one_minute_rate = calc_rate(@one_minute_rate, ONE_MINUTE_FACTOR, count)
@@ -72,19 +55,29 @@ module Metrics
72
55
  @initialized = true
73
56
  end
74
57
 
75
- @uncounted = 0
58
+ @count = 0
76
59
  end
77
60
 
78
- def one_minute_rate
79
- @one_minute_rate * @ratemultiplier
61
+ def one_minute_rate(rate_unit = :seconds)
62
+ scale_to_ns @one_minute_rate, rate_unit
80
63
  end
81
64
 
82
- def five_minute_rate
83
- @five_minute_rate * @ratemultiplier
65
+ def five_minute_rate(rate_unit = :seconds)
66
+ scale_to_ns @five_minute_rate, rate_unit
84
67
  end
85
68
 
86
- def fifteen_minute_rate
87
- @fifteen_minute_rate * @ratemultiplier
69
+ def fifteen_minute_rate(rate_unit = :seconds)
70
+ scale_to_ns @fifteen_minute_rate, rate_unit
71
+ end
72
+
73
+ def mean_rate(rate_unit = seconds)
74
+ count = @count
75
+ if count == 0
76
+ return 0.0;
77
+ else
78
+ elapsed = Time.now.to_f - @start_time.to_f
79
+ scale_to_ns (count / elapsed), rate_unit
80
+ end
88
81
  end
89
82
 
90
83
  def to_s
@@ -94,6 +87,24 @@ module Metrics
94
87
  :fifteen_minute_rate => self.fifteen_minute_rate
95
88
  }.to_json
96
89
  end
90
+
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
97
108
  end
98
109
  end
99
110
  end
@@ -0,0 +1,108 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'time_units')
2
+
3
+ module Metrics
4
+ module Instruments
5
+ class Timer < Base
6
+ include Metrics::TimeConversion
7
+
8
+ attr_reader :duration_unit, :rate_unit
9
+
10
+ def initialize(duration_unit = :seconds, rate_unit = :seconds)
11
+ @meter = Meter.new
12
+ @histogram = ExponentialHistogram.new
13
+
14
+ @duration_unit = duration_unit
15
+ @rate_unit = rate_unit
16
+
17
+ clear
18
+ end
19
+
20
+ def clear
21
+ @histogram.clear
22
+ end
23
+
24
+ def update(duration, unit)
25
+ mult = convert_to_ns(1, unit)
26
+ self.update_timer (duration * mult)
27
+ end
28
+
29
+ def time(&block)
30
+ start_time = Time.now.to_f
31
+ result = block.call
32
+ time_diff = Time.now.to_f - start_time
33
+ update_timer(time_diff)
34
+ result
35
+ end
36
+
37
+ def count
38
+ @histogram.count
39
+ end
40
+
41
+ def fifteen_minute_rate
42
+ @meter.fifteen_minute_rate(@rate_unit)
43
+ end
44
+
45
+ def five_minute_rate
46
+ @meter.five_minute_rate(@rate_unit)
47
+ end
48
+
49
+ def one_minute_rate
50
+ @meter.one_minute_rate(@rate_unit)
51
+ end
52
+
53
+ def mean_rate
54
+ @meter.mean_rate(@rate_unit)
55
+ end
56
+
57
+ def max
58
+ scale_duration_to_ns @histogram.max, @duration_unit
59
+ end
60
+
61
+ def min
62
+ scale_duration_to_ns @histogram.min, @duration_unit
63
+ end
64
+
65
+ def mean
66
+ scale_duration_to_ns @histogram.mean, @duration_unit
67
+ end
68
+
69
+ def std_dev
70
+ scale_duration_to_ns @histogram.std_dev, @duration_unit
71
+ end
72
+
73
+ def quantiles(percentiles = [0.99,0.97,0.95,0.75,0.5,0.25])
74
+ result = {}
75
+
76
+ @histogram.quantiles(percentiles).each do |k,v|
77
+ result[k] = scale_duration_to_ns v, @duration_unit
78
+ end
79
+
80
+ result
81
+ end
82
+
83
+ def values
84
+ result = []
85
+
86
+ @histogram.values.each do |value|
87
+ result << (scale_duration_to_ns value, @duration_unit)
88
+ end
89
+
90
+ result
91
+ end
92
+
93
+ def update_timer(duration)
94
+ if duration >= 0
95
+ @histogram.update(duration)
96
+ @meter.mark
97
+ end
98
+ end
99
+
100
+ private
101
+ def scale_duration_to_ns(value, unit)
102
+ value / convert_to_ns(1, unit)
103
+ end
104
+
105
+ end
106
+ end
107
+ end
108
+
@@ -1,3 +1,5 @@
1
+ require File.join(File.dirname(__FILE__), 'time_units')
2
+
1
3
  require File.join(File.dirname(__FILE__), 'statistics', 'sample')
2
4
  require File.join(File.dirname(__FILE__), 'statistics', 'uniform_sample')
3
5
  require File.join(File.dirname(__FILE__), 'statistics', 'exponential_sample')
@@ -7,6 +9,7 @@ require File.join(File.dirname(__FILE__), 'instruments', 'counter')
7
9
  require File.join(File.dirname(__FILE__), 'instruments', 'meter')
8
10
  require File.join(File.dirname(__FILE__), 'instruments', 'gauge')
9
11
  require File.join(File.dirname(__FILE__), 'instruments', 'histogram')
12
+ require File.join(File.dirname(__FILE__), 'instruments', 'timer')
10
13
 
11
14
 
12
15
  require 'json'
@@ -7,7 +7,7 @@ module Metrics
7
7
  @count = 0
8
8
  @size = size
9
9
  @alpha = alpha
10
- @rescale_window = "1 hour".to("seconds").scalar
10
+ @rescale_window = 1.hour.to_seconds.to_i
11
11
  self.clear
12
12
  end
13
13
 
@@ -0,0 +1,61 @@
1
+ module Metrics
2
+
3
+ class TimeUnit
4
+ def self.to_nsec(mult = 1)
5
+ raise NotImplementedError
6
+ end
7
+ end
8
+
9
+ class Nanoseconds < TimeUnit
10
+ def self.to_nsec(mult = 1)
11
+ mult
12
+ end
13
+ end
14
+
15
+ class Microseconds < TimeUnit
16
+ def self.to_nsec(mult = 1)
17
+ 1000 * mult
18
+ end
19
+ end
20
+
21
+ class Milliseconds < TimeUnit
22
+ def self.to_nsec(mult = 1)
23
+ 1000000 * mult
24
+ end
25
+ end
26
+
27
+ class Seconds < TimeUnit
28
+ def self.to_nsec(mult = 1)
29
+ 1000000000 * mult
30
+ end
31
+ end
32
+
33
+ class Minutes < TimeUnit
34
+ def self.to_nsec(mult = 1)
35
+ 60000000000 * mult
36
+ end
37
+ end
38
+
39
+ class Hours < TimeUnit
40
+ def self.to_nsec(mult = 1)
41
+ 3600000000000 * mult
42
+ end
43
+ end
44
+
45
+ module TimeConversion
46
+
47
+
48
+ def convert_to_ns(value, unit)
49
+ units = {
50
+ :nanoseconds => Nanoseconds,
51
+ :microseconds => Microseconds,
52
+ :milliseconds => Milliseconds,
53
+ :seconds => Seconds,
54
+ :minutes => Minutes,
55
+ :hours => Hours
56
+ }
57
+
58
+ return units[unit].to_nsec * value
59
+ end
60
+ end
61
+ end
@@ -1,3 +1,3 @@
1
1
  module Metrics
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
3
3
  end
data/lib/ruby-metrics.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # == Metrics Initialization
2
2
  #
3
+ require 'quantity/all'
3
4
 
4
5
  module Metrics
5
6
 
@@ -12,4 +13,10 @@ module Metrics
12
13
 
13
14
  end
14
15
 
16
+ Quantity::Unit.add_unit :minute, :time, 60000, :minutes
17
+ Quantity::Unit.add_unit :hour, :time, 3600000, :hours
18
+ Quantity::Unit.add_unit :day, :time, 86400000, :days
19
+ Quantity::Unit.add_unit :week, :time, 604800000, :weeks
20
+
21
+
15
22
  require File.join(File.dirname(__FILE__), 'ruby-metrics', 'agent')
data/ruby-metrics.gemspec CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
15
15
  s.rubyforge_project = "ruby-metrics"
16
16
 
17
17
  s.add_dependency "json"
18
- s.add_dependency "ruby-units"
18
+ s.add_dependency "quantity"
19
19
 
20
20
  s.add_development_dependency "rspec"
21
21
  s.add_development_dependency "simplecov", [">= 0.3.8"] #, :require => false
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'ruby-units'
3
2
 
4
3
  describe Metrics::Instruments::Meter do
5
4
  before(:each) do
@@ -19,23 +18,22 @@ describe Metrics::Instruments::Meter do
19
18
  meter = Metrics::Instruments::Meter.new
20
19
  meter.mark(500)
21
20
  meter.counted.should == 500
22
- meter.uncounted.should == 500
23
21
  end
24
22
 
25
23
  it "should accept options for the constructor" do
26
- meter = Metrics::Instruments::Meter.new({:interval => "10 seconds", :rateunit => "5 seconds"})
24
+ meter = Metrics::Instruments::Meter.new
27
25
  end
28
26
 
29
27
  context "A timer with an initial mark of 3 at a 1 second rate unit on a 5 second interval" do
30
28
 
31
29
  before(:each) do
32
- @meter = Metrics::Instruments::Meter.new({:interval => "5 seconds", :rateunit => "1 second"})
30
+ @meter = Metrics::Instruments::Meter.new
33
31
  @meter.mark(3)
34
32
  @meter.tick()
35
33
  end
36
34
 
37
35
  def tick_for(time)
38
- count = ((time.to("seconds")) / 5).scalar
36
+ count = ((time.to_seconds) / 5).to_i
39
37
  (1..count).each do
40
38
  @meter.tick()
41
39
  end
@@ -47,7 +45,7 @@ describe Metrics::Instruments::Meter do
47
45
  end
48
46
 
49
47
  it "should have a rate of 0.22072766470286553 events/sec after 1 minute" do
50
- tick_for("1 minute")
48
+ tick_for(1.minute)
51
49
  @meter.one_minute_rate.should == 0.22072766470286553
52
50
  end
53
51
  end
@@ -58,7 +56,7 @@ describe Metrics::Instruments::Meter do
58
56
  end
59
57
 
60
58
  it "should have a rate of 0.49123845184678905 events/sec after 1 minute" do
61
- tick_for("1 minute")
59
+ tick_for(1.minute)
62
60
  @meter.five_minute_rate.should == 0.49123845184678905
63
61
  end
64
62
  end
@@ -67,9 +65,17 @@ describe Metrics::Instruments::Meter do
67
65
  it "should have a rate of 0.6 events/sec after the first tick" do
68
66
  @meter.fifteen_minute_rate.should == 0.6
69
67
  end
68
+
69
+ it "should have a rate of 36.0 events per minute after the first tick" do
70
+ @meter.fifteen_minute_rate(:minutes).should == 36.0
71
+ end
72
+
73
+ it "should have a rate of 2160.0 events per hour after the first tick" do
74
+ @meter.fifteen_minute_rate(:hours).should == 2160.0
75
+ end
70
76
 
71
77
  it "should have a rate of 0.5613041910189706 events/sec after 1 minute" do
72
- tick_for("1 minute")
78
+ tick_for(1.minute)
73
79
  @meter.fifteen_minute_rate.should == 0.5613041910189706
74
80
  end
75
81
  end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metrics::Instruments::Timer do
4
+ before(:each) do
5
+ Thread.stub!(:new).and_return do |block|
6
+ # Do nothing
7
+ end
8
+ end
9
+
10
+ context "An empty timer" do
11
+ before(:each) do
12
+ @timer = Metrics::Instruments::Timer.new()
13
+ end
14
+
15
+ it "should have a max of zero" do
16
+ @timer.max.should == 0
17
+ end
18
+
19
+ it "should have a min of zero" do
20
+ @timer.min.should == 0
21
+ end
22
+
23
+ it "should have a mean of zero" do
24
+ @timer.mean.should == 0
25
+ end
26
+
27
+ it "should have a min of zero" do
28
+ @timer.std_dev.should == 0
29
+ end
30
+
31
+ it "should have quantiles of zero" do
32
+ @timer.quantiles.should == {0.99=>0.0, 0.97=>0.0, 0.95=>0.0, 0.75=>0.0, 0.5=>0.0, 0.25=>0.0}
33
+ end
34
+
35
+ it "should have a mean rate of zero" do
36
+ @timer.mean_rate.should == 0
37
+ end
38
+
39
+ it "should have a one-minute rate of zero" do
40
+ @timer.one_minute_rate.should == 0
41
+ end
42
+
43
+ it "should have a five-minute rate of zero" do
44
+ @timer.five_minute_rate.should == 0
45
+ end
46
+
47
+ it "should have a fifteen-minute rate of zero" do
48
+ @timer.fifteen_minute_rate.should == 0
49
+ end
50
+
51
+ it "should have no values stored" do
52
+ @timer.values.length.should == 0
53
+ end
54
+
55
+ end
56
+
57
+ context "Timing some events" do
58
+ before(:each) do
59
+ @timer = Metrics::Instruments::Timer.new(:milliseconds, :seconds)
60
+ @timer.update(10, :milliseconds)
61
+ @timer.update(20, :milliseconds)
62
+ @timer.update(20, :milliseconds)
63
+ @timer.update(30, :milliseconds)
64
+ @timer.update(40, :milliseconds)
65
+ end
66
+
67
+ it "should have counted 5 events" do
68
+ @timer.count.should == 5
69
+ end
70
+
71
+ it "should accurately calculate the minimum duration" do
72
+ @timer.min.should == 10
73
+ end
74
+
75
+ it "should accurately calculate the maximum duration" do
76
+ @timer.max.should == 40
77
+ end
78
+
79
+ it "should accurately calculate the mean duration" do
80
+ @timer.mean.should == 24
81
+ end
82
+
83
+ it "should accurately calculate the standard deviation of the durations" do
84
+ @timer.std_dev.should == 11.401754901476078
85
+ end
86
+
87
+ it "should accurately calculate percentiles" do
88
+ @timer.quantiles.should == {0.99=>39.6, 0.97=>38.8, 0.95=>38.0, 0.75=>30.0, 0.5=>20.0, 0.25=>20.0}
89
+ end
90
+
91
+ it "should contain the series added" do
92
+ @timer.values.sort.should == [10, 20, 20, 30, 40]
93
+ end
94
+ end
95
+
96
+ context "Timing blocks of code" do
97
+ before(:each) do
98
+ @timer = Metrics::Instruments::Timer.new(:nanoseconds, :nanoseconds)
99
+ end
100
+
101
+ it "should return the result of the block passed" do
102
+ result = @timer.time do
103
+ sleep(5)
104
+ "narf"
105
+ end
106
+
107
+ result.should == "narf"
108
+
109
+ @timer.max.should >= 5.0
110
+ @timer.max.should <= 6.0
111
+
112
+ end
113
+ end
114
+
115
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Metrics::TimeUnit do
4
+ it "should raise NotImplementedError for #to_nsec" do
5
+ begin
6
+ Metrics::TimeUnit.to_nsec
7
+ rescue NotImplementedError
8
+ true
9
+ else
10
+ fail
11
+ end
12
+ end
13
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 6
7
+ - 7
8
8
  - 0
9
- version: 0.6.0
9
+ version: 0.7.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-15 00:00:00 -07:00
17
+ date: 2011-04-18 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -31,7 +31,7 @@ dependencies:
31
31
  prerelease: false
32
32
  version_requirements: *id001
33
33
  - !ruby/object:Gem::Dependency
34
- name: ruby-units
34
+ name: quantity
35
35
  requirement: &id002 !ruby/object:Gem::Requirement
36
36
  none: false
37
37
  requirements:
@@ -101,10 +101,12 @@ files:
101
101
  - lib/ruby-metrics/instruments/gauge.rb
102
102
  - lib/ruby-metrics/instruments/histogram.rb
103
103
  - lib/ruby-metrics/instruments/meter.rb
104
+ - lib/ruby-metrics/instruments/timer.rb
104
105
  - lib/ruby-metrics/logging.rb
105
106
  - lib/ruby-metrics/statistics/exponential_sample.rb
106
107
  - lib/ruby-metrics/statistics/sample.rb
107
108
  - lib/ruby-metrics/statistics/uniform_sample.rb
109
+ - lib/ruby-metrics/time_units.rb
108
110
  - lib/ruby-metrics/version.rb
109
111
  - ruby-metrics.gemspec
110
112
  - spec/agent_spec.rb
@@ -113,11 +115,13 @@ files:
113
115
  - spec/instruments/gauge_spec.rb
114
116
  - spec/instruments/histogram_spec.rb
115
117
  - spec/instruments/meter_spec.rb
118
+ - spec/instruments/timer_spec.rb
116
119
  - spec/instruments_spec.rb
117
120
  - spec/spec_helper.rb
118
121
  - spec/statistics/exponential_sample_spec.rb
119
122
  - spec/statistics/sample_spec.rb
120
123
  - spec/statistics/uniform_sample_spec.rb
124
+ - spec/time_units_spec.rb
121
125
  has_rdoc: true
122
126
  homepage: https://github.com/johnewart/ruby-metrics
123
127
  licenses: []
@@ -132,7 +136,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
132
136
  requirements:
133
137
  - - ">="
134
138
  - !ruby/object:Gem::Version
135
- hash: 764509393290573048
139
+ hash: -24047821763014180
136
140
  segments:
137
141
  - 0
138
142
  version: "0"
@@ -141,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
145
  requirements:
142
146
  - - ">="
143
147
  - !ruby/object:Gem::Version
144
- hash: 764509393290573048
148
+ hash: -24047821763014180
145
149
  segments:
146
150
  - 0
147
151
  version: "0"
@@ -159,8 +163,10 @@ test_files:
159
163
  - spec/instruments/gauge_spec.rb
160
164
  - spec/instruments/histogram_spec.rb
161
165
  - spec/instruments/meter_spec.rb
166
+ - spec/instruments/timer_spec.rb
162
167
  - spec/instruments_spec.rb
163
168
  - spec/spec_helper.rb
164
169
  - spec/statistics/exponential_sample_spec.rb
165
170
  - spec/statistics/sample_spec.rb
166
171
  - spec/statistics/uniform_sample_spec.rb
172
+ - spec/time_units_spec.rb