metriks 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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