ruby-metrics 0.9.0 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '04971d78af7ca682008c7d66db2eac34111c089bb17778cc18ff434843707126'
4
+ data.tar.gz: e1ec094ef4a2082d12df838b211fac9cebaf207bf4fabb33fb3b30eaef1f97ff
5
+ SHA512:
6
+ metadata.gz: 60b859a4037eb7f22d2bbaa0965cc1ed55845817a613701a9a3f19bba088c6886feb68aad020e421506d330fb87eafa612b0c364773d850dbcd92d9e95798e56
7
+ data.tar.gz: 1b2f48bee677fadfffb53335058aa036985f64f9ce2f6f7901bae3fe8b996dc26c36b83d0303d1ea93065e5eef6cf298ff8c4328c8c13df0182592b694c2d02a
data/Rakefile CHANGED
@@ -21,7 +21,7 @@ task :release => :build do
21
21
  sh 'git', 'tag', '-m', "Version #{Metrics::VERSION}", "v#{Metrics::VERSION}"
22
22
  sh "git push origin master"
23
23
  sh "git push origin v#{Metrics::VERSION}"
24
- #sh "ls pkg/*.gem | xargs -n 1 gem push"
24
+ sh "ls pkg/*.gem | xargs -n 1 gem push"
25
25
  end
26
26
 
27
27
  RSpec::Core::RakeTask.new do |t|
@@ -67,6 +67,10 @@ module Metrics
67
67
  @reporter = Reporter.new({:agent => self, :delay => delay})
68
68
  end
69
69
 
70
+ def stop_reporting
71
+ @reporter.stop
72
+ end
73
+
70
74
  def as_json(*_)
71
75
  @instruments
72
76
  end
@@ -1,11 +1,14 @@
1
+ require_relative 'instrument'
2
+
1
3
  module Metrics
2
4
  module Instruments
3
- class Counter
5
+ class Counter < Instrument
4
6
 
5
7
  attr_reader :units
6
8
 
7
9
  def initialize(options = {})
8
10
  @value = 0
11
+ @units = options[:units]
9
12
  end
10
13
 
11
14
  def inc(value = 1)
@@ -1,6 +1,8 @@
1
+ require_relative 'instrument'
2
+
1
3
  module Metrics
2
4
  module Instruments
3
- class Gauge
5
+ class Gauge < Instrument
4
6
  attr_reader :units
5
7
 
6
8
  def initialize(options = {}, &block)
@@ -1,23 +1,24 @@
1
+ require_relative 'instrument'
1
2
  require 'ruby-metrics/statistics/uniform_sample'
2
3
  require 'ruby-metrics/statistics/exponential_sample'
3
4
 
4
5
  module Metrics
5
6
  module Instruments
6
- class Histogram
7
+ class Histogram < Instrument
8
+ attr_reader :count
7
9
 
8
10
  def initialize(type = :uniform)
9
- @count = 0
10
- case type
11
- when :uniform
12
- @sample = Metrics::Statistics::UniformSample.new
13
- when :exponential
14
- @sample = Metrics::Statistics::ExponentialSample.new
15
- end
16
- @min = nil
17
- @max = nil
18
- @sum = 0
19
- @variance_s = 0
20
- @variance_m = -1
11
+ @sample =
12
+ case type
13
+ when :uniform
14
+ Metrics::Statistics::UniformSample.new
15
+ when :exponential
16
+ Metrics::Statistics::ExponentialSample.new
17
+ else
18
+ raise ArgumentError, "Unknown type #{type.inspect}"
19
+ end
20
+
21
+ clear
21
22
  end
22
23
 
23
24
  def update(value)
@@ -26,7 +27,7 @@ module Metrics
26
27
  @sample.update(value)
27
28
  update_max(value)
28
29
  update_min(value)
29
- update_variance(value);
30
+ update_variance(value)
30
31
  end
31
32
 
32
33
  def clear
@@ -40,111 +41,80 @@ module Metrics
40
41
  end
41
42
 
42
43
  def quantiles(percentiles)
43
- # Calculated using the same logic as R and Ecxel use
44
+ # Calculated using the same logic as R and Excel use
44
45
  # as outlined by the NIST here: http://www.itl.nist.gov/div898/handbook/prc/section2/prc252.htm
45
- count = @count
46
- scores = {}
47
- values = @sample.values[0..count-1]
48
46
 
47
+ sorted_values = @sample.values[0...@count].sort
48
+ scores = { }
49
49
  percentiles.each do |pct|
50
- scores[pct] = 0.0
51
- end
52
-
53
- if count > 0
54
- values.sort!
55
- percentiles.each do |pct|
56
- idx = pct * (values.length - 1) + 1.0
57
- if idx <= 1
58
- scores[pct] = values[0]
59
- elsif idx >= values.length
60
- scores[pct] = values[values.length-1]
50
+ scores[pct] =
51
+ if @count == 0
52
+ 0.0
61
53
  else
62
- lower = values[idx.to_i - 1]
63
- upper = values[idx.to_i]
64
- scores[pct] = lower + (idx - idx.floor) * (upper - lower)
65
- end
54
+ index = pct * (sorted_values.length - 1) + 1.0
55
+ if index <= 1
56
+ sorted_values.first
57
+ elsif index >= sorted_values.length
58
+ sorted_values.last
59
+ else
60
+ lower = sorted_values[index.to_i - 1]
61
+ upper = sorted_values[index.to_i]
62
+ lower + (index - index.floor) * (upper - lower)
63
+ end
66
64
  end
67
65
  end
68
-
69
- return scores
66
+ scores
70
67
  end
71
68
 
72
69
  def update_min(value)
73
- if (@min == nil || value < @min)
70
+ if @min.nil? || value < @min
74
71
  @min = value
75
72
  end
76
73
  end
77
74
 
78
75
  def update_max(value)
79
- if (@max == nil || value > @max)
76
+ if @max.nil? || value > @max
80
77
  @max = value
81
78
  end
82
79
  end
83
80
 
84
81
  def update_variance(value)
85
- count = @count
86
- old_m = @variance_m
87
- new_m = @variance_m + ((value - old_m) / count)
88
- new_s = @variance_s + ((value - old_m) * (value - new_m))
82
+ new_m = @variance_m + ((value - @variance_m) / @count)
83
+ new_s = @variance_s + ((value - @variance_m) * (value - new_m))
89
84
 
90
85
  @variance_m = new_m
91
86
  @variance_s = new_s
92
87
  end
93
88
 
94
89
  def variance
95
- count = @count
96
- variance_s = @variance_s
97
-
98
- if count <= 1
99
- return 0.0
90
+ if @count <= 1
91
+ 0.0
100
92
  else
101
- return variance_s.to_f / (count - 1).to_i
93
+ @variance_s.to_f / (@count - 1)
102
94
  end
103
95
  end
104
96
 
105
- def count
106
- count = @count
107
- return count
108
- end
109
-
110
-
111
97
  def max
112
- max = @max
113
- if max != nil
114
- return max
115
- else
116
- return 0.0
117
- end
98
+ @max || 0.0
118
99
  end
119
100
 
120
101
  def min
121
- min = @min
122
- if min != nil
123
- return min
124
- else
125
- return 0.0
126
- end
102
+ @min || 0.0
127
103
  end
128
104
 
129
105
  def mean
130
- count = @count
131
- sum = @sum
132
-
133
- if count > 0
134
- return sum / count
106
+ if @count > 0
107
+ @sum / @count
135
108
  else
136
- return 0.0
109
+ 0.0
137
110
  end
138
111
  end
139
112
 
140
113
  def std_dev
141
- count = @count
142
- variance = self.variance()
143
-
144
- if count > 0
145
- return Math.sqrt(variance)
114
+ if @count > 0
115
+ Math.sqrt(variance)
146
116
  else
147
- return 0.0
117
+ 0.0
148
118
  end
149
119
  end
150
120
 
@@ -154,11 +124,11 @@ module Metrics
154
124
 
155
125
  def as_json(*_)
156
126
  {
157
- :min => self.min,
158
- :max => self.max,
159
- :mean => self.mean,
160
- :variance => self.variance,
161
- :percentiles => self.quantiles([0.25, 0.50, 0.75, 0.95, 0.97, 0.98, 0.99])
127
+ :min => min,
128
+ :max => max,
129
+ :mean => mean,
130
+ :variance => variance,
131
+ :percentiles => quantiles(Timer::DEFAULT_PERCENTILES)
162
132
  }
163
133
  end
164
134
 
@@ -0,0 +1,13 @@
1
+ module Metrics
2
+ module Instruments
3
+ class Instrument
4
+ def tags
5
+ @tags ||= {}
6
+ end
7
+
8
+ def tag(key, value)
9
+ tags[key] = value
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,42 +1,41 @@
1
+ require_relative 'instrument'
1
2
  require 'ruby-metrics/time_units'
2
3
 
3
4
  module Metrics
4
5
  module Instruments
5
- class Meter
6
+ class Meter < Instrument
6
7
  include Metrics::TimeConversion
7
8
 
8
9
  # From http://www.teamquest.com/pdfs/whitepaper/ldavg2.pdf
9
- INTERVAL = 5.0
10
- INTERVAL_IN_NS = 5000000000.0
11
- ONE_MINUTE_FACTOR = 1 - Math.exp(-INTERVAL / 60.0)
12
- FIVE_MINUTE_FACTOR = 1 - Math.exp(-INTERVAL / (60.0 * 5.0))
13
- FIFTEEN_MINUTE_FACTOR = 1 - Math.exp(-INTERVAL / (60.0 * 15.0))
14
-
15
- attr_reader :count
16
- attr_reader :units
10
+ INTERVAL_SECONDS = 5.0
11
+ ONE_MINUTE_FACTOR = 1 - Math.exp(-INTERVAL_SECONDS / 60.0)
12
+ FIVE_MINUTE_FACTOR = 1 - Math.exp(-INTERVAL_SECONDS / (60.0 * 5.0))
13
+ FIFTEEN_MINUTE_FACTOR = 1 - Math.exp(-INTERVAL_SECONDS / (60.0 * 15.0))
14
+
15
+ attr_reader :count, :units
17
16
  alias_method :counted, :count
18
17
 
19
18
  def initialize(options = {})
20
- @one_minute_rate = @five_minute_rate = @fifteen_minute_rate = 0.0
21
- @count = 0
22
- @initialized = false
23
- @start_time = Time.now.to_f
24
- @units = "#{options[:units]}"
19
+ @units = options[:units]
20
+ clear
25
21
 
26
22
  @timer_thread = Thread.new do
27
23
  begin
28
24
  loop do
29
- self.tick
30
- sleep(INTERVAL)
25
+ tick
26
+ sleep(INTERVAL_SECONDS)
31
27
  end
32
28
  rescue Exception => e
33
29
  logger.error "Error in timer thread: #{e.class.name}: #{e}\n #{e.backtrace.join("\n ")}"
34
- end # begin
35
- end # thread new
36
-
30
+ end
31
+ end
37
32
  end
38
33
 
39
34
  def clear
35
+ @one_minute_rate = @five_minute_rate = @fifteen_minute_rate = 0.0
36
+ @count = 0
37
+ @initialized = false
38
+ @start_time = Time.now.to_f
40
39
  end
41
40
 
42
41
  def mark(count = 1)
@@ -44,19 +43,18 @@ module Metrics
44
43
  end
45
44
 
46
45
  def calc_rate(rate, factor, count)
47
- rate = rate.to_f + (factor.to_f * (count.to_f - rate.to_f))
48
- rate.to_f
46
+ rate.to_f + factor.to_f * (count.to_f - rate.to_f)
49
47
  end
50
48
 
51
49
  def tick
52
- count = @count.to_f / Seconds.to_nsec(INTERVAL).to_f
50
+ count = @count.to_f / Seconds.to_nsec(INTERVAL_SECONDS).to_f
53
51
 
54
- if (@initialized)
52
+ if @initialized
55
53
  @one_minute_rate = calc_rate(@one_minute_rate, ONE_MINUTE_FACTOR, count)
56
54
  @five_minute_rate = calc_rate(@five_minute_rate, FIVE_MINUTE_FACTOR, count)
57
55
  @fifteen_minute_rate = calc_rate(@fifteen_minute_rate, FIFTEEN_MINUTE_FACTOR, count)
58
56
  else
59
- @one_minute_rate = @five_minute_rate = @fifteen_minute_rate = (count)
57
+ @one_minute_rate = @five_minute_rate = @fifteen_minute_rate = count
60
58
  @initialized = true
61
59
  end
62
60
 
@@ -76,21 +74,20 @@ module Metrics
76
74
  end
77
75
 
78
76
  def mean_rate(rate_unit = :seconds)
79
- count = @count
80
- if count == 0
81
- return 0.0
77
+ if @count == 0
78
+ 0.0
82
79
  else
83
80
  elapsed = Time.now.to_f - @start_time.to_f
84
- mult = scale_time_units(:seconds, rate_unit)
85
- count.to_f / (mult * elapsed.to_f)
81
+ scale_factor = scale_time_units(:seconds, rate_unit)
82
+ @count.to_f / (elapsed * scale_factor)
86
83
  end
87
84
  end
88
85
 
89
86
  def as_json(*_)
90
87
  {
91
- :one_minute_rate => self.one_minute_rate,
92
- :five_minute_rate => self.five_minute_rate,
93
- :fifteen_minute_rate => self.fifteen_minute_rate
88
+ :one_minute_rate => one_minute_rate,
89
+ :five_minute_rate => five_minute_rate,
90
+ :fifteen_minute_rate => fifteen_minute_rate
94
91
  }
95
92
  end
96
93
 
@@ -1,12 +1,15 @@
1
- require File.join(File.dirname(__FILE__), '..', 'time_units')
1
+ require_relative 'instrument'
2
+ require 'ruby-metrics/time_units'
2
3
 
3
4
  module Metrics
4
5
  module Instruments
5
- class Timer
6
+ class Timer < Instrument
6
7
  include Metrics::TimeConversion
7
8
 
8
9
  attr_reader :duration_unit, :rate_unit, :units
9
10
 
11
+ DEFAULT_PERCENTILES = [0.25, 0.50, 0.75, 0.95, 0.97, 0.98, 0.99]
12
+
10
13
  def initialize(options = {})
11
14
  @meter = Meter.new
12
15
  @histogram = ExponentialHistogram.new
@@ -19,17 +22,17 @@ module Metrics
19
22
  end
20
23
 
21
24
  def clear
25
+ @meter.clear
22
26
  @histogram.clear
23
27
  end
24
28
 
25
29
  def update(duration, unit)
26
- mult = convert_to_ns(1, unit)
27
- self.update_timer(duration * mult)
30
+ update_timer(convert_to_ns(duration, unit))
28
31
  end
29
32
 
30
- def time(&block)
33
+ def time
31
34
  start_time = Time.now.to_f
32
- result = block.call
35
+ result = yield
33
36
  time_diff = Time.now.to_f - start_time
34
37
  time_in_ns = convert_to_ns time_diff, :seconds
35
38
  update_timer(time_in_ns)
@@ -72,24 +75,17 @@ module Metrics
72
75
  scale_duration_to_ns @histogram.std_dev, @duration_unit
73
76
  end
74
77
 
75
- def quantiles(percentiles = [0.99,0.97,0.95,0.75,0.5,0.25])
76
- result = {}
77
-
78
- @histogram.quantiles(percentiles).each do |k,v|
78
+ def quantiles(percentiles = DEFAULT_PERCENTILES)
79
+ @histogram.quantiles(percentiles).inject({}) do |result, (k, v)|
79
80
  result[k] = scale_duration_to_ns v, @duration_unit
81
+ result
80
82
  end
81
-
82
- result
83
83
  end
84
84
 
85
85
  def values
86
- result = []
87
-
88
- @histogram.values.each do |value|
89
- result << (scale_duration_to_ns value, @duration_unit)
86
+ @histogram.values.map do |value|
87
+ scale_duration_to_ns value, @duration_unit
90
88
  end
91
-
92
- result
93
89
  end
94
90
 
95
91
  def update_timer(duration)
@@ -112,7 +108,7 @@ module Metrics
112
108
  :min => min,
113
109
  :max => max,
114
110
  :mean => mean,
115
- :percentiles => quantiles([0.25, 0.50, 0.75, 0.95, 0.97, 0.98, 0.99]),
111
+ :percentiles => quantiles,
116
112
  :unit => @duration_unit
117
113
  }
118
114
  }
@@ -124,7 +120,7 @@ module Metrics
124
120
 
125
121
  private
126
122
  def scale_duration_to_ns(value, unit)
127
- value.to_f / convert_to_ns(1, unit).to_f
123
+ value.to_f / convert_to_ns(1.0, unit)
128
124
  end
129
125
  end
130
126
  end
@@ -12,7 +12,7 @@ module Metrics
12
12
  :requests, :uncaught_exceptions,
13
13
  :status_codes
14
14
 
15
- def initialize(options ={})
15
+ def initialize(options = {})
16
16
  @options = options
17
17
  @agent = @options.delete(:agent) || Agent.new
18
18
  end
@@ -15,54 +15,41 @@ module Metrics
15
15
  module Rack
16
16
  class Middleware
17
17
 
18
- attr_accessor :app, :options, :agent,
19
- # integration metrics
20
- :requests, :uncaught_exceptions,
21
- :status_codes
22
-
18
+ attr_accessor :app, :options, :agent
19
+
23
20
  def initialize(app, options ={})
24
21
  @app = app
25
22
  @options = {:show => "/stats"}.merge(options)
26
23
  @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
24
  end
41
-
25
+
42
26
  def call(env)
43
27
  return show(env) if show?(env)
44
28
 
45
29
  env['metrics.agent'] = @agent
46
30
 
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
-
31
+ status, headers, body = time_request { @app.call(env) }
32
+
33
+ incr_status_code_counter(status / 100)
34
+
53
35
  [status, headers, body]
54
36
  rescue Exception
55
37
  # TODO: add "last_uncaught_exception" with string of error
56
- self.uncaught_exceptions.incr
38
+ incr_uncaught_exceptions
57
39
  raise
58
40
  end
59
-
60
- def show?(env, test = self.options[:show])
41
+
42
+ private
43
+ def show?(env, test = options[:show])
61
44
  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
45
+ when String === test
46
+ env['PATH_INFO'] == test
47
+ when Regexp === test
48
+ env['PATH_INFO'] =~ test
49
+ when test.respond_to?(:call)
50
+ test.call(env)
51
+ else
52
+ test
66
53
  end
67
54
  end
68
55
 
@@ -75,7 +62,18 @@ module Metrics
75
62
  [body]
76
63
  ]
77
64
  end
78
-
65
+
66
+ def time_request(&block)
67
+ @agent.timer(:_requests).time(&block)
68
+ end
69
+
70
+ def incr_uncaught_exceptions
71
+ @agent.counter(:_uncaught_exceptions).incr
72
+ end
73
+
74
+ def incr_status_code_counter(hundred)
75
+ @agent.counter(:"_status_#{hundred}xx").incr
76
+ end
79
77
  end
80
78
  end
81
79
  end
@@ -5,7 +5,12 @@ module Metrics
5
5
 
6
6
  include Logging
7
7
 
8
+ def stop
9
+ @running = false
10
+ end
11
+
8
12
  def initialize(options = {})
13
+ @running = true
9
14
 
10
15
  if options[:agent] == nil
11
16
  raise "Need an agent to report data from"
@@ -15,13 +20,13 @@ module Metrics
15
20
  agent = options[:agent]
16
21
 
17
22
  Thread.new {
18
- while(true)
23
+ while @running
19
24
  agent.reporters.each do |name, service|
20
25
  service.report(agent)
21
26
  end
22
27
  sleep delay
23
28
  end
24
- }.join
29
+ }
25
30
  end
26
31
 
27
32
  end