rubycut-metriks 0.9.9.4
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/Gemfile +14 -0
- data/LICENSE +21 -0
- data/README.md +405 -0
- data/Rakefile +150 -0
- data/benchmark/samplers.rb +92 -0
- data/lib/metriks/counter.rb +44 -0
- data/lib/metriks/ewma.rb +63 -0
- data/lib/metriks/exponentially_decaying_sample.rb +102 -0
- data/lib/metriks/histogram.rb +112 -0
- data/lib/metriks/meter.rb +85 -0
- data/lib/metriks/registry.rb +207 -0
- data/lib/metriks/reporter/graphite.rb +119 -0
- data/lib/metriks/reporter/librato_metrics.rb +185 -0
- data/lib/metriks/reporter/logger.rb +129 -0
- data/lib/metriks/reporter/proc_title.rb +65 -0
- data/lib/metriks/reporter/riemann.rb +119 -0
- data/lib/metriks/simple_moving_average.rb +60 -0
- data/lib/metriks/snapshot.rb +59 -0
- data/lib/metriks/time_tracker.rb +26 -0
- data/lib/metriks/timer.rb +101 -0
- data/lib/metriks/uniform_sample.rb +40 -0
- data/lib/metriks/utilization_timer.rb +43 -0
- data/lib/metriks.rb +35 -0
- data/metriks.gemspec +100 -0
- data/test/counter_test.rb +39 -0
- data/test/graphite_reporter_test.rb +41 -0
- data/test/histogram_test.rb +199 -0
- data/test/librato_metrics_reporter_test.rb +35 -0
- data/test/logger_reporter_test.rb +49 -0
- data/test/meter_test.rb +38 -0
- data/test/metriks_test.rb +31 -0
- data/test/proc_title_reporter_test.rb +25 -0
- data/test/registry_test.rb +49 -0
- data/test/riemann_reporter_test.rb +88 -0
- data/test/test_helper.rb +33 -0
- data/test/thread_error_handling_tests.rb +20 -0
- data/test/timer_test.rb +32 -0
- data/test/utilization_timer_test.rb +25 -0
- metadata +161 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'atomic'
|
2
|
+
|
3
|
+
module Metriks
|
4
|
+
# Public: Counters are one of the simplest metrics whose only operations
|
5
|
+
# are increment and decrement.
|
6
|
+
class Counter
|
7
|
+
# Public: Initialize a new Counter.
|
8
|
+
def initialize
|
9
|
+
@count = Atomic.new(0)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Public: Reset the counter back to 0
|
13
|
+
#
|
14
|
+
# Returns nothing.
|
15
|
+
def clear
|
16
|
+
@count.value = 0
|
17
|
+
end
|
18
|
+
|
19
|
+
# Public: Increment the counter.
|
20
|
+
#
|
21
|
+
# incr - The value to add to the counter.
|
22
|
+
#
|
23
|
+
# Returns nothing.
|
24
|
+
def increment(incr = 1)
|
25
|
+
@count.update { |v| v + incr }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Public: Decrement the counter.
|
29
|
+
#
|
30
|
+
# decr - The value to subtract from the counter.
|
31
|
+
#
|
32
|
+
# Returns nothing.
|
33
|
+
def decrement(decr = 1)
|
34
|
+
@count.update { |v| v - decr }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: The current count.
|
38
|
+
#
|
39
|
+
# Returns the count.
|
40
|
+
def count
|
41
|
+
@count.value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/metriks/ewma.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'atomic'
|
2
|
+
|
3
|
+
module Metriks
|
4
|
+
class EWMA
|
5
|
+
INTERVAL = 5.0
|
6
|
+
SECONDS_PER_MINUTE = 60.0
|
7
|
+
|
8
|
+
ONE_MINUTE = 1
|
9
|
+
FIVE_MINUTES = 5
|
10
|
+
FIFTEEN_MINUTES = 15
|
11
|
+
|
12
|
+
M1_ALPHA = 1 - Math.exp(-INTERVAL / SECONDS_PER_MINUTE / ONE_MINUTE)
|
13
|
+
M5_ALPHA = 1 - Math.exp(-INTERVAL / SECONDS_PER_MINUTE / FIVE_MINUTES)
|
14
|
+
M15_ALPHA = 1 - Math.exp(-INTERVAL / SECONDS_PER_MINUTE / FIFTEEN_MINUTES)
|
15
|
+
|
16
|
+
def self.new_m1
|
17
|
+
new(M1_ALPHA, INTERVAL)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.new_m5
|
21
|
+
new(M5_ALPHA, INTERVAL)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.new_m15
|
25
|
+
new(M15_ALPHA, INTERVAL)
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(alpha, interval)
|
29
|
+
@alpha = alpha
|
30
|
+
@interval = interval
|
31
|
+
|
32
|
+
@initialized = false
|
33
|
+
@rate = Atomic.new(0.0)
|
34
|
+
@uncounted = Atomic.new(0)
|
35
|
+
end
|
36
|
+
|
37
|
+
def clear
|
38
|
+
@initialized = false
|
39
|
+
@rate.value = 0.0
|
40
|
+
@uncounted.value = 0
|
41
|
+
end
|
42
|
+
|
43
|
+
def update(value)
|
44
|
+
@uncounted.update { |v| v + value }
|
45
|
+
end
|
46
|
+
|
47
|
+
def tick
|
48
|
+
count = @uncounted.swap(0)
|
49
|
+
instant_rate = count / @interval.to_f
|
50
|
+
|
51
|
+
if @initialized
|
52
|
+
@rate.update { |v| v + @alpha * (instant_rate - v) }
|
53
|
+
else
|
54
|
+
@rate.value = instant_rate
|
55
|
+
@initialized = true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def rate
|
60
|
+
@rate.value
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'atomic'
|
2
|
+
require 'red_black_tree'
|
3
|
+
require 'metriks/snapshot'
|
4
|
+
|
5
|
+
module Metriks
|
6
|
+
class ExponentiallyDecayingSample
|
7
|
+
RESCALE_THRESHOLD = 60 * 60 # 1 hour
|
8
|
+
|
9
|
+
def initialize(reservoir_size, alpha, values = nil)
|
10
|
+
@values = values || RedBlackTree.new
|
11
|
+
@count = Atomic.new(0)
|
12
|
+
@next_scale_time = Atomic.new(0)
|
13
|
+
@alpha = alpha
|
14
|
+
@reservoir_size = reservoir_size
|
15
|
+
@mutex = Mutex.new
|
16
|
+
clear
|
17
|
+
end
|
18
|
+
|
19
|
+
def clear
|
20
|
+
@mutex.synchronize do
|
21
|
+
@values.clear
|
22
|
+
@count.value = 0
|
23
|
+
@next_scale_time.value = Time.now + RESCALE_THRESHOLD
|
24
|
+
@start_time = Time.now
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def size
|
29
|
+
count = @count.value
|
30
|
+
count < @reservoir_size ? count : @reservoir_size
|
31
|
+
end
|
32
|
+
|
33
|
+
def snapshot
|
34
|
+
@mutex.synchronize do
|
35
|
+
Snapshot.new(@values.values)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def update(value, timestamp = Time.now)
|
40
|
+
@mutex.synchronize do
|
41
|
+
priority = weight(timestamp - @start_time) / rand
|
42
|
+
priority = Float::MAX if priority.infinite?
|
43
|
+
new_count = @count.update { |v| v + 1 }
|
44
|
+
|
45
|
+
if priority.nan?
|
46
|
+
warn "ExponentiallyDecayingSample found priority of NaN. timestamp: #{timestamp.to_f} start_time: #{@start_time.to_f}"
|
47
|
+
return
|
48
|
+
end
|
49
|
+
|
50
|
+
if new_count <= @reservoir_size
|
51
|
+
@values[priority] = value
|
52
|
+
else
|
53
|
+
first_priority = @values.first[0]
|
54
|
+
if first_priority < priority
|
55
|
+
unless @values[priority]
|
56
|
+
@values[priority] = value
|
57
|
+
|
58
|
+
until @values.delete(first_priority)
|
59
|
+
first_priority = @values.first[0]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
now = Time.new
|
67
|
+
next_time = @next_scale_time.value
|
68
|
+
if now >= next_time
|
69
|
+
rescale(now, next_time)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def weight(time)
|
74
|
+
Math.exp(@alpha * time)
|
75
|
+
end
|
76
|
+
|
77
|
+
def rescale(now, next_time)
|
78
|
+
if @next_scale_time.compare_and_swap(next_time, now + RESCALE_THRESHOLD)
|
79
|
+
@mutex.synchronize do
|
80
|
+
old_start_time = @start_time
|
81
|
+
@start_time = Time.now
|
82
|
+
@values.keys.each do |key|
|
83
|
+
value = @values.delete(key)
|
84
|
+
new_key = key * Math.exp(-@alpha * (@start_time - old_start_time))
|
85
|
+
|
86
|
+
if key.nan?
|
87
|
+
warn "ExponentiallyDecayingSample found a key of NaN. old_start_time: #{old_start_time.to_f} start_time: #{@start_time.to_f}"
|
88
|
+
next
|
89
|
+
end
|
90
|
+
|
91
|
+
if new_key.nan?
|
92
|
+
warn "ExponentiallyDecayingSample found a new_key of NaN. key: #{key} old_start_time: #{old_start_time.to_f} start_time: #{@start_time.to_f}"
|
93
|
+
next
|
94
|
+
end
|
95
|
+
|
96
|
+
@values[new_key] = value
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'atomic'
|
2
|
+
require 'metriks/uniform_sample'
|
3
|
+
require 'metriks/exponentially_decaying_sample'
|
4
|
+
|
5
|
+
module Metriks
|
6
|
+
class Histogram
|
7
|
+
DEFAULT_SAMPLE_SIZE = 1028
|
8
|
+
DEFAULT_ALPHA = 0.015
|
9
|
+
|
10
|
+
def self.new_uniform
|
11
|
+
new(Metriks::UniformSample.new(DEFAULT_SAMPLE_SIZE))
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.new_exponentially_decaying
|
15
|
+
new(Metriks::ExponentiallyDecayingSample.new(DEFAULT_SAMPLE_SIZE, DEFAULT_ALPHA))
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(sample)
|
19
|
+
@sample = sample
|
20
|
+
@count = Atomic.new(0)
|
21
|
+
@min = Atomic.new(nil)
|
22
|
+
@max = Atomic.new(nil)
|
23
|
+
@sum = Atomic.new(0)
|
24
|
+
@variance = Atomic.new([ -1, 0 ])
|
25
|
+
end
|
26
|
+
|
27
|
+
def clear
|
28
|
+
@sample.clear
|
29
|
+
@count.value = 0
|
30
|
+
@min.value = nil
|
31
|
+
@max.value = nil
|
32
|
+
@sum.value = 0
|
33
|
+
@variance.value = [ -1, 0 ]
|
34
|
+
end
|
35
|
+
|
36
|
+
def update(value)
|
37
|
+
@count.update { |v| v + 1 }
|
38
|
+
@sample.update(value)
|
39
|
+
self.max = value
|
40
|
+
self.min = value
|
41
|
+
@sum.update { |v| v + value }
|
42
|
+
update_variance(value)
|
43
|
+
end
|
44
|
+
|
45
|
+
def snapshot
|
46
|
+
@sample.snapshot
|
47
|
+
end
|
48
|
+
|
49
|
+
def count
|
50
|
+
@count.value
|
51
|
+
end
|
52
|
+
|
53
|
+
def max
|
54
|
+
count > 0 ? @max.value : 0.0
|
55
|
+
end
|
56
|
+
|
57
|
+
def min
|
58
|
+
count > 0 ? @min.value : 0.0
|
59
|
+
end
|
60
|
+
|
61
|
+
def mean
|
62
|
+
count > 0 ? @sum.value / count : 0.0
|
63
|
+
end
|
64
|
+
|
65
|
+
def stddev
|
66
|
+
count > 0 ? variance ** 0.5 : 0.0
|
67
|
+
end
|
68
|
+
|
69
|
+
def variance
|
70
|
+
count <= 1 ? 0.0 : @variance.value[1] / (count - 1)
|
71
|
+
end
|
72
|
+
|
73
|
+
def max=(potential_max)
|
74
|
+
done = false
|
75
|
+
|
76
|
+
while !done
|
77
|
+
current_max = @max.value
|
78
|
+
done = (!current_max.nil? && current_max >= potential_max) || @max.compare_and_swap(current_max, potential_max)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def min=(potential_min)
|
83
|
+
done = false
|
84
|
+
|
85
|
+
while !done
|
86
|
+
current_min = @min.value
|
87
|
+
done = (!current_min.nil? && current_min <= potential_min) || @min.compare_and_swap(current_min, potential_min)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def update_variance(value)
|
92
|
+
@variance.update do |old_values|
|
93
|
+
new_values = Array.new(2)
|
94
|
+
if old_values[0] == -1
|
95
|
+
new_values[0] = value
|
96
|
+
new_values[1] = 0
|
97
|
+
else
|
98
|
+
old_m = old_values[0]
|
99
|
+
old_s = old_values[1]
|
100
|
+
|
101
|
+
new_m = old_m + ((value - old_m) / count)
|
102
|
+
new_s = old_s + ((value - old_m) * (value - new_m))
|
103
|
+
|
104
|
+
new_values[0] = new_m
|
105
|
+
new_values[1] = new_s
|
106
|
+
end
|
107
|
+
|
108
|
+
new_values
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'atomic'
|
2
|
+
|
3
|
+
require 'metriks/ewma'
|
4
|
+
|
5
|
+
module Metriks
|
6
|
+
class Meter
|
7
|
+
TICK_INTERVAL = 5.0
|
8
|
+
|
9
|
+
def initialize(averager_klass = Metriks::EWMA)
|
10
|
+
@count = Atomic.new(0)
|
11
|
+
@start_time = Time.now.to_f
|
12
|
+
@last_tick = Atomic.new(@start_time)
|
13
|
+
|
14
|
+
@m1_rate = averager_klass.new_m1
|
15
|
+
@m5_rate = averager_klass.new_m5
|
16
|
+
@m15_rate = averager_klass.new_m15
|
17
|
+
end
|
18
|
+
|
19
|
+
def clear
|
20
|
+
@count.value = 0
|
21
|
+
@start_time = Time.now.to_f
|
22
|
+
@last_tick.value = @start_time
|
23
|
+
@m1_rate.clear
|
24
|
+
@m5_rate.clear
|
25
|
+
@m15_rate.clear
|
26
|
+
end
|
27
|
+
|
28
|
+
def tick
|
29
|
+
@m1_rate.tick
|
30
|
+
@m5_rate.tick
|
31
|
+
@m15_rate.tick
|
32
|
+
end
|
33
|
+
|
34
|
+
def tick_if_nessesary
|
35
|
+
old_tick = @last_tick.value
|
36
|
+
new_tick = Time.new.to_f
|
37
|
+
age = new_tick - old_tick
|
38
|
+
if age > TICK_INTERVAL && @last_tick.compare_and_swap(old_tick, new_tick)
|
39
|
+
required_ticks = age / TICK_INTERVAL
|
40
|
+
required_ticks.to_i.times do
|
41
|
+
tick
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def mark(val = 1)
|
47
|
+
tick_if_nessesary
|
48
|
+
@count.update { |v| v + val }
|
49
|
+
@m1_rate.update(val)
|
50
|
+
@m5_rate.update(val)
|
51
|
+
@m15_rate.update(val)
|
52
|
+
end
|
53
|
+
|
54
|
+
def count
|
55
|
+
@count.value
|
56
|
+
end
|
57
|
+
|
58
|
+
def one_minute_rate
|
59
|
+
tick_if_nessesary
|
60
|
+
@m1_rate.rate
|
61
|
+
end
|
62
|
+
|
63
|
+
def five_minute_rate
|
64
|
+
tick_if_nessesary
|
65
|
+
@m5_rate.rate
|
66
|
+
end
|
67
|
+
|
68
|
+
def fifteen_minute_rate
|
69
|
+
tick_if_nessesary
|
70
|
+
@m15_rate.rate
|
71
|
+
end
|
72
|
+
|
73
|
+
def mean_rate
|
74
|
+
if count == 0
|
75
|
+
return 0.0
|
76
|
+
else
|
77
|
+
elapsed = Time.now.to_f - @start_time
|
78
|
+
count / elapsed
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def stop
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'metriks/counter'
|
2
|
+
require 'metriks/timer'
|
3
|
+
require 'metriks/utilization_timer'
|
4
|
+
require 'metriks/meter'
|
5
|
+
require 'metriks/gauge'
|
6
|
+
|
7
|
+
module Metriks
|
8
|
+
# Public: A collection of metrics
|
9
|
+
class Registry
|
10
|
+
# Public: The default registry for the process.
|
11
|
+
#
|
12
|
+
# Returns the default Registry for the process.
|
13
|
+
def self.default
|
14
|
+
@default ||= new
|
15
|
+
end
|
16
|
+
|
17
|
+
# Public: Initializes a new Registry.
|
18
|
+
def initialize
|
19
|
+
@mutex = Mutex.new
|
20
|
+
@metrics = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Public: Clear all of the metrics in the Registry. This ensures all
|
24
|
+
# metrics that have been added are stopped.
|
25
|
+
#
|
26
|
+
# Returns nothing.
|
27
|
+
def clear
|
28
|
+
@mutex.synchronize do
|
29
|
+
@metrics.each do |key, metric|
|
30
|
+
metric.stop if metric.respond_to?(:stop)
|
31
|
+
end
|
32
|
+
|
33
|
+
@metrics = {}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Clear all of the metrics in the Registry. This has the same
|
38
|
+
# effect as calling #clear.
|
39
|
+
#
|
40
|
+
# Returns nothing.
|
41
|
+
def stop
|
42
|
+
clear
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: Iterate over all of the counters.
|
46
|
+
#
|
47
|
+
# Examples
|
48
|
+
#
|
49
|
+
# registry.each do |name, metric|
|
50
|
+
# puts name
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# Returns nothing.
|
54
|
+
def each(&block)
|
55
|
+
metrics = @mutex.synchronize do
|
56
|
+
@metrics.dup
|
57
|
+
end
|
58
|
+
|
59
|
+
metrics.each(&block)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Public: Fetch or create a new counter metric. Counters are one of the
|
63
|
+
# simplest metrics whose only operations are increment and decrement.
|
64
|
+
#
|
65
|
+
# name - The String name of the metric to define or fetch
|
66
|
+
#
|
67
|
+
# Examples
|
68
|
+
#
|
69
|
+
# registry.counter('method.calls')
|
70
|
+
#
|
71
|
+
# Returns the Metriks::Counter identified by the name.
|
72
|
+
def counter(name)
|
73
|
+
add_or_get(name, Metriks::Counter)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Public: Fetch or create a new gauge metric.
|
77
|
+
#
|
78
|
+
# name - The String name of the metric to define or fetch
|
79
|
+
#
|
80
|
+
# Examples
|
81
|
+
#
|
82
|
+
# registry.gauge('disk_space.used') { 1 }
|
83
|
+
#
|
84
|
+
# Returns the Metriks::Gauge identified by the name.
|
85
|
+
def gauge(name, callable = nil, &block)
|
86
|
+
add_or_get(name, Metriks::Gauge) do
|
87
|
+
Metriks::Gauge.new(callable, &block)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Public: Fetch or create a new meter metric. Meters are a counter that
|
92
|
+
# tracks throughput along with the count.
|
93
|
+
#
|
94
|
+
# name - The String name of the metric to define or fetch
|
95
|
+
#
|
96
|
+
# Examples
|
97
|
+
#
|
98
|
+
# registry.meter('resque.calls')
|
99
|
+
#
|
100
|
+
# Returns the Metriks::Meter identified by the name.
|
101
|
+
def meter(name)
|
102
|
+
add_or_get(name, Metriks::Meter)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Public: Fetch or create a new timer metric. Timers provide the means to
|
106
|
+
# time the execution of a method including statistics on the number of
|
107
|
+
# invocations, average length of time, throughput.
|
108
|
+
#
|
109
|
+
# name - The String name of the metric to define or fetch
|
110
|
+
#
|
111
|
+
# Examples
|
112
|
+
#
|
113
|
+
# registry.timer('resque.worker')
|
114
|
+
#
|
115
|
+
# Returns the Metriks::Timer identified by the name.
|
116
|
+
def timer(name)
|
117
|
+
add_or_get(name, Metriks::Timer)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Public: Fetch or create a new utilization timer metric.
|
121
|
+
#
|
122
|
+
# Utilization timers are a specialized version of a timer that calculate
|
123
|
+
# the percentage of wall-clock time (between 0 and 1) that was spent in
|
124
|
+
# the method. This metric is most valuable in a single-threaded
|
125
|
+
# environment where a processes is waiting on an external resource like a
|
126
|
+
# message queue or HTTP server.
|
127
|
+
#
|
128
|
+
# name - The String name of the metric to define or fetch
|
129
|
+
#
|
130
|
+
# Examples
|
131
|
+
#
|
132
|
+
# registry.utilization_timer('rack.utilization')
|
133
|
+
#
|
134
|
+
# Returns the Metriks::UtilizationTimer identified by the name.
|
135
|
+
def utilization_timer(name)
|
136
|
+
add_or_get(name, Metriks::UtilizationTimer)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Public: Fetch or create a new histogram metric. Histograms record values
|
140
|
+
# and expose statistics about the distribution of the data like median and
|
141
|
+
# 95th percentile.
|
142
|
+
#
|
143
|
+
# name - The String name of the metric to define or fetch
|
144
|
+
#
|
145
|
+
# Examples
|
146
|
+
#
|
147
|
+
# registry.histogram('backlog.wait')
|
148
|
+
#
|
149
|
+
# Returns the Metriks::Histogram identified by the name.
|
150
|
+
def histogram(name)
|
151
|
+
add_or_get(name, Metriks::Histogram) do
|
152
|
+
Metriks::Histogram.new_exponentially_decaying
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Public: Fetch an existing metric.
|
157
|
+
#
|
158
|
+
# name - The String name of the metric to fetch
|
159
|
+
#
|
160
|
+
# Examples
|
161
|
+
#
|
162
|
+
# registry.get('rack.utilization')
|
163
|
+
#
|
164
|
+
# Returns the metric or nil.
|
165
|
+
def get(name)
|
166
|
+
@mutex.synchronize do
|
167
|
+
@metrics[name]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Public: Add a new metric.
|
172
|
+
#
|
173
|
+
# name - The String name of the metric to add
|
174
|
+
# metric - The metric instance to add
|
175
|
+
#
|
176
|
+
# Examples
|
177
|
+
#
|
178
|
+
# registry.add('method.calls', Metriks::Counter.new)
|
179
|
+
#
|
180
|
+
# Returns nothing.
|
181
|
+
# Raises RuntimeError if the metric name is already defined
|
182
|
+
def add(name, metric)
|
183
|
+
@mutex.synchronize do
|
184
|
+
if @metrics[name]
|
185
|
+
raise "Metric '#{name}' already defined"
|
186
|
+
else
|
187
|
+
@metrics[name] = metric
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
protected
|
193
|
+
def add_or_get(name, klass, &create_metric)
|
194
|
+
@mutex.synchronize do
|
195
|
+
if metric = @metrics[name]
|
196
|
+
if !metric.is_a?(klass)
|
197
|
+
raise "Metric already defined as '#{metric.class}'"
|
198
|
+
else
|
199
|
+
return metric
|
200
|
+
end
|
201
|
+
else
|
202
|
+
@metrics[name] = create_metric ? create_metric.call : klass.new
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|