metriks 0.8.1

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.
@@ -0,0 +1,27 @@
1
+
2
+ module Metriks
3
+ VERSION = '0.8.1'
4
+
5
+ def self.get(name)
6
+ Metriks::Registry.default.get(name)
7
+ end
8
+
9
+ def self.counter(name)
10
+ Metriks::Registry.default.counter(name)
11
+ end
12
+
13
+ def self.timer(name)
14
+ Metriks::Registry.default.timer(name)
15
+ end
16
+
17
+ def self.utilization_timer(name)
18
+ Metriks::Registry.default.utilization_timer(name)
19
+ end
20
+
21
+ def self.meter(name)
22
+ Metriks::Registry.default.meter(name)
23
+ end
24
+ end
25
+
26
+ require 'metriks/registry'
27
+ require 'metriks/reporter/proc_title'
@@ -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
@@ -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,103 @@
1
+ require 'atomic'
2
+ require 'metriks/uniform_sample'
3
+
4
+ module Metriks
5
+ class Histogram
6
+ DEFAULT_SAMPLE_SIZE = 1028
7
+ DEFAULT_ALPHA = 0.015
8
+
9
+ def self.new_uniform
10
+ new(Metriks::UniformSample.new(DEFAULT_SAMPLE_SIZE))
11
+ end
12
+
13
+ def initialize(sample)
14
+ @sample = sample
15
+ @count = Atomic.new(0)
16
+ @min = Atomic.new(nil)
17
+ @max = Atomic.new(nil)
18
+ @sum = Atomic.new(0)
19
+ @variance = Atomic.new([ -1, 0 ])
20
+ end
21
+
22
+ def clear
23
+ @sample.clear
24
+ @count.value = 0
25
+ @min.value = nil
26
+ @max.value = nil
27
+ @sum.value = 0
28
+ @variance.value = [ -1, 0 ]
29
+ end
30
+
31
+ def update(value)
32
+ @count.update { |v| v + 1 }
33
+ @sample.update(value)
34
+ self.max = value
35
+ self.min = value
36
+ @sum.update { |v| v + value }
37
+ update_variance(value)
38
+ end
39
+
40
+ def count
41
+ @count.value
42
+ end
43
+
44
+ def max
45
+ count > 0 ? @max.value : 0.0
46
+ end
47
+
48
+ def min
49
+ count > 0 ? @min.value : 0.0
50
+ end
51
+
52
+ def mean
53
+ count > 0 ? @sum.value / count : 0.0
54
+ end
55
+
56
+ def stddev
57
+ count > 0 ? variance : 0.0
58
+ end
59
+
60
+ def variance
61
+ count <= 1 ? 0.0 : @variance.value[1] / (count - 1)
62
+ end
63
+
64
+ def max=(potential_max)
65
+ done = false
66
+
67
+ while !done
68
+ current_max = @max.value
69
+ done = (!current_max.nil? && current_max >= potential_max) || @max.compare_and_swap(current_max, potential_max)
70
+ end
71
+ end
72
+
73
+ def min=(potential_min)
74
+ done = false
75
+
76
+ while !done
77
+ current_min = @min.value
78
+ done = (!current_min.nil? && current_min <= potential_min) || @min.compare_and_swap(current_min, potential_min)
79
+ end
80
+ end
81
+
82
+ def update_variance(value)
83
+ @variance.update do |old_values|
84
+ new_values = Array.new(2)
85
+ if old_values[0] == -1
86
+ new_values[0] = value
87
+ new_values[1] = 0
88
+ else
89
+ old_m = old_values[0]
90
+ old_s = old_values[1]
91
+
92
+ new_m = old_m + ((value - old_m) / count)
93
+ new_s = old_s + ((value - old_m) * (value - new_m))
94
+
95
+ new_values[0] = new_m
96
+ new_values[1] = new_s
97
+ end
98
+
99
+ new_values
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,73 @@
1
+ require 'atomic'
2
+
3
+ require 'metriks/ewma'
4
+
5
+ module Metriks
6
+ class Meter
7
+ def initialize(averager_klass = Metriks::EWMA)
8
+ @count = Atomic.new(0)
9
+ @start_time = Time.now
10
+
11
+ @m1_rate = averager_klass.new_m1
12
+ @m5_rate = averager_klass.new_m5
13
+ @m15_rate = averager_klass.new_m15
14
+
15
+ @thread = Thread.new do
16
+ loop do
17
+ sleep averager_klass::INTERVAL
18
+ tick
19
+ end
20
+ end
21
+ end
22
+
23
+ def clear
24
+ @count.value = 0
25
+ @start_time = Time.now
26
+ @m1_rate.clear
27
+ @m5_rate.clear
28
+ @m15_rate.clear
29
+ end
30
+
31
+ def tick
32
+ @m1_rate.tick
33
+ @m5_rate.tick
34
+ @m15_rate.tick
35
+ end
36
+
37
+ def mark(val = 1)
38
+ @count.update { |v| v + val }
39
+ @m1_rate.update(val)
40
+ @m5_rate.update(val)
41
+ @m15_rate.update(val)
42
+ end
43
+
44
+ def count
45
+ @count.value
46
+ end
47
+
48
+ def one_minute_rate
49
+ @m1_rate.rate
50
+ end
51
+
52
+ def five_minute_rate
53
+ @m5_rate.rate
54
+ end
55
+
56
+ def fifteen_minute_rate
57
+ @m15_rate.rate
58
+ end
59
+
60
+ def mean_rate
61
+ if count == 0
62
+ return 0.0
63
+ else
64
+ elapsed = Time.now - @start_time
65
+ count / elapsed
66
+ end
67
+ end
68
+
69
+ def stop
70
+ @thread.kill
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,174 @@
1
+ require 'metriks/counter'
2
+ require 'metriks/timer'
3
+ require 'metriks/utilization_timer'
4
+ require 'metriks/meter'
5
+
6
+ module Metriks
7
+ # Public: A collection of metrics
8
+ class Registry
9
+ # Public: The default registry for the process.
10
+ #
11
+ # Returns the default Registry for the process.
12
+ def self.default
13
+ @default ||= new
14
+ end
15
+
16
+ # Public: Initializes a new Registry.
17
+ def initialize
18
+ @mutex = Mutex.new
19
+ @metrics = {}
20
+ end
21
+
22
+ # Public: Clear all of the metrics in the Registry. This ensures all
23
+ # metrics that have been added are stopped.
24
+ #
25
+ # Returns nothing.
26
+ def clear
27
+ @mutex.synchronize do
28
+ @metrics.each do |key, metric|
29
+ metric.stop if metric.respond_to?(:stop)
30
+ end
31
+
32
+ @metrics = {}
33
+ end
34
+ end
35
+
36
+ # Public: Clear all of the metrics in the Registry. This has the same
37
+ # effect as calling #clear.
38
+ #
39
+ # Returns nothing.
40
+ def stop
41
+ clear
42
+ end
43
+
44
+ # Public: Iterate over all of the counters.
45
+ #
46
+ # Examples
47
+ #
48
+ # registry.each do |name, metric|
49
+ # puts name
50
+ # end
51
+ #
52
+ # Returns nothing.
53
+ def each(&block)
54
+ metrics = @mutex.synchronize do
55
+ @metrics.dup
56
+ end
57
+
58
+ metrics.each(&block)
59
+ end
60
+
61
+ # Public: Fetch or create a new counter metric. Counters are one of the
62
+ # simplest metrics whose only operations are increment and decrement.
63
+ #
64
+ # name - The String name of the metric to define or fetch
65
+ #
66
+ # Examples
67
+ #
68
+ # registry.counter('method.calls')
69
+ #
70
+ # Returns the Metricks::Counter identified by the name.
71
+ def counter(name)
72
+ add_or_get(name, Metriks::Counter)
73
+ end
74
+
75
+ # Public: Fetch or create a new meter metric. Meters are a counter that
76
+ # tracks throughput along with the count.
77
+ #
78
+ # name - The String name of the metric to define or fetch
79
+ #
80
+ # Examples
81
+ #
82
+ # registry.meter('resque.calls')
83
+ #
84
+ # Returns the Metricks::Meter identified by the name.
85
+ def meter(name)
86
+ add_or_get(name, Metriks::Meter)
87
+ end
88
+
89
+ # Public: Fetch or create a new timer metric. Timers provide the means to
90
+ # time the execution of a method including statistics on the number of
91
+ # invocations, average length of time, throughput.
92
+ #
93
+ # name - The String name of the metric to define or fetch
94
+ #
95
+ # Examples
96
+ #
97
+ # registry.timer('resque.worker')
98
+ #
99
+ # Returns the Metricks::Timer identified by the name.
100
+ def timer(name)
101
+ add_or_get(name, Metriks::Timer)
102
+ end
103
+
104
+ # Public: Fetch or create a new utilization timer metric.
105
+ #
106
+ # Utilization timers are a specialized version of a timer that calculate
107
+ # the percentage of wall-clock time (between 0 and 1) that was spent in
108
+ # the method. This metric is most valuable in a single-threaded
109
+ # environment where a processes is waiting on an external resource like a
110
+ # message queue or HTTP server.
111
+ #
112
+ # name - The String name of the metric to define or fetch
113
+ #
114
+ # Examples
115
+ #
116
+ # registry.utilization_timer('rack.utilization')
117
+ #
118
+ # Returns the Metricks::UtilizationTimer identified by the name.
119
+ def utilization_timer(name)
120
+ add_or_get(name, Metriks::UtilizationTimer)
121
+ end
122
+
123
+ # Public: Fetch an existing metric.
124
+ #
125
+ # name - The String name of the metric to fetch
126
+ #
127
+ # Examples
128
+ #
129
+ # registry.get('rack.utilization')
130
+ #
131
+ # Returns the metric or nil.
132
+ def get(name)
133
+ @mutex.synchronize do
134
+ @metrics[name]
135
+ end
136
+ end
137
+
138
+ # Public: Add a new metric.
139
+ #
140
+ # name - The String name of the metric to add
141
+ # metric - The metric instance to add
142
+ #
143
+ # Examples
144
+ #
145
+ # registry.add('method.calls', Metriks::Counter.new)
146
+ #
147
+ # Returns nothing.
148
+ # Raises RuntimeError if the metric name is already defined
149
+ def add(name, metric)
150
+ @mutex.synchronize do
151
+ if @metrics[name]
152
+ raise "Metric '#{name}' already defined"
153
+ else
154
+ @metrics[name] = metric
155
+ end
156
+ end
157
+ end
158
+
159
+ protected
160
+ def add_or_get(name, klass)
161
+ @mutex.synchronize do
162
+ if metric = @metrics[name]
163
+ if !metric.is_a?(klass)
164
+ raise "Metric already defined as '#{metric.class}'"
165
+ else
166
+ return metric
167
+ end
168
+ else
169
+ @metrics[name] = klass.new
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end