rubycut-metriks 0.9.9.4

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'rake', '0.8.7'
7
+ gem 'riemann-client', '~> 0.0.7'
8
+ gem 'rbtree', :platform => :mri_18
9
+ end
10
+
11
+ group :development do
12
+ gem 'guard'
13
+ gem 'guard-test'
14
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2012 Eric Lindvall
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,405 @@
1
+ # Metriks Client
2
+
3
+ This is an experiment in making a threadsafe, low impact library to measure
4
+ aspects of your ruby.
5
+
6
+ The library is very much a work-in-progress. It is being developed as
7
+ I find needs while developing [Papertrail](https://papertrailapp.com/).
8
+
9
+
10
+ # Installing
11
+
12
+ The API is still in flux, but you can add this to your project by installing
13
+ the gem.
14
+
15
+ To install, add this to your `Gemfile`:
16
+
17
+ ``` ruby
18
+ gem 'metriks'
19
+ ```
20
+
21
+ and re-run `bundle`.
22
+
23
+
24
+ # Metric API Overview
25
+
26
+ ## Counters
27
+
28
+ Basic atomic counter. Used as an underlying metric for many of the other
29
+ more advanced metrics.
30
+
31
+
32
+ ### increment(incr = 1)
33
+
34
+ Increment the counter. Without an argument it will increment by `1`.
35
+
36
+ ``` ruby
37
+ counter = Metriks.counter('calls')
38
+ counter.increment
39
+ ```
40
+
41
+ ### decrement(decr = 1)
42
+
43
+ Decrement the counter. Without an argument it will decrement by `1`.
44
+
45
+ ``` ruby
46
+ counter = Metriks.counter('calls')
47
+ counter.decrement
48
+ ```
49
+
50
+ #### count()
51
+
52
+ Return the current value of the counter.
53
+
54
+ ``` ruby
55
+ counter = Metriks.counter('calls')
56
+ puts "counter: #{counter.count}"
57
+ ```
58
+
59
+ ## Gauges
60
+
61
+ A gauge is an instantaneous measurement of a value.
62
+
63
+ It takes a callback to measure the value in form of a block or a callable
64
+ object.
65
+
66
+ **WARNING:** The code in the callback is executed every time the `#value`
67
+ method is called on the gauge. Most of the time this will be done by a
68
+ metriks reporter that is running in a separate thread.
69
+
70
+ ``` ruby
71
+ # Callback as block
72
+ gauge = Metriks.gauge('queue.size') { queue.size }
73
+
74
+ # Callback as object responding to #call
75
+ callable = proc { queue.size }
76
+ gauge = Metriks.gauge('queue.size', callable)
77
+ ```
78
+
79
+ ### set(val)
80
+
81
+ Set the current value.
82
+
83
+ ``` ruby
84
+ gauge = Metriks.gauge('queue_size')
85
+ gauge.set(queue.size)
86
+ ```
87
+
88
+ ### value()
89
+
90
+ Returns the value returned by the callback (if one is defined), returns the
91
+ value set via `#set` (or the default of 0) otherwise.
92
+
93
+ ``` ruby
94
+ gauge = Metriks.gauge('queue_size')
95
+ puts "queue size: #{gauge.value}"
96
+ ```
97
+
98
+ ## Meters
99
+
100
+ A meter that measures the mean throughput and the one-, five-, and
101
+ fifteen-minute exponentially-weighted moving average throughputs.
102
+
103
+ ### mark(val = 1)
104
+
105
+ Record an event with the meter. Without an argument it will record one event.
106
+
107
+ ``` ruby
108
+ meter = Metriks.meter('requests')
109
+ meter.mark
110
+ ```
111
+
112
+ ### count()
113
+
114
+ Returns the total number of events that have been recorded.
115
+
116
+ ``` ruby
117
+ meter = Metriks.meter('requests')
118
+ puts "total: #{meter.count}"
119
+ ```
120
+
121
+ ### one_minute_rate()
122
+
123
+ Returns the one-minute average rate.
124
+
125
+ ``` ruby
126
+ meter = Metriks.meter('requests')
127
+ puts "rate: #{meter.one_minute_rate}/sec"
128
+ ```
129
+
130
+ ### five_minute_rate()
131
+
132
+ Returns the five-minute average rate.
133
+
134
+ ``` ruby
135
+ meter = Metriks.meter('requests')
136
+ puts "rate: #{meter.five_minute_rate}/sec"
137
+ ```
138
+
139
+ ### fifteen_minute_rate()
140
+
141
+ Returns the fifteen-minute average rate.
142
+
143
+ ``` ruby
144
+ meter = Metriks.meter('requests')
145
+ puts "rate: #{meter.fifteen_minute_rate}/sec"
146
+ ```
147
+
148
+ ### mean_rate()
149
+
150
+ Returns the mean (average) rate of the events since the start of the process.
151
+
152
+ ``` ruby
153
+ meter = Metriks.meter('requests')
154
+ puts "rate: #{meter.mean_rate}/sec"
155
+ ```
156
+
157
+ ## Timers
158
+
159
+ A timer that measures the average time as well as throughput metrics via
160
+ a meter.
161
+
162
+ ### update(duration)
163
+
164
+ Records the duration of an operation. This normally wouldn't need to be
165
+ called — the `#time` method is provided to simplify recording a duration.
166
+
167
+ ``` ruby
168
+ timer = Metriks.timer('requests')
169
+ t0 = Time.now
170
+ work
171
+ timer.update(Time.now - t0)
172
+ ```
173
+
174
+ ### time(callable = nil, &block)
175
+
176
+ Measure the amount of time a proc takes to execute. Takes either a block
177
+ or an object responding to `#call` (normally a `proc` or `lambda`).
178
+
179
+ ``` ruby
180
+ timer = Metriks.timer('requests')
181
+ timer.time do
182
+ work
183
+ end
184
+ ```
185
+
186
+ If neither a block or an object is passed to the method, an object that
187
+ responds to `#stop` will be returned. When `#stop` is called, the time
188
+ will be recorded.
189
+
190
+ ``` ruby
191
+ timer = Metriks.timer('requests')
192
+ t = timer.time
193
+ work
194
+ t.stop
195
+ ```
196
+
197
+ ### count()
198
+
199
+ Returns the number of measurements that have been made.
200
+
201
+ ``` ruby
202
+ timer = Metriks.timer('requests')
203
+ puts "calls: #{timer.count}"
204
+ ```
205
+
206
+ ### one_minute_rate()
207
+
208
+ Returns the one-minute average rate.
209
+
210
+ ``` ruby
211
+ meter = Metriks.timer('requests')
212
+ puts "rate: #{meter.one_minute_rate}/sec"
213
+ ```
214
+
215
+ ### five_minute_rate()
216
+
217
+ Returns the five-minute average rate.
218
+
219
+ ``` ruby
220
+ meter = Metriks.timer('requests')
221
+ puts "rate: #{meter.five_minute_rate}/sec"
222
+ ```
223
+
224
+ ### fifteen_minute_rate()
225
+
226
+ Returns the fifteen-minute average rate.
227
+
228
+ ``` ruby
229
+ meter = Metriks.timer('requests')
230
+ puts "rate: #{meter.fifteen_minute_rate}/sec"
231
+ ```
232
+
233
+ ### mean_rate()
234
+
235
+ Returns the mean (average) rate of the events since the start of the process.
236
+
237
+ ``` ruby
238
+ meter = Metriks.timer('requests')
239
+ puts "rate: #{meter.mean_rate}/sec"
240
+ ```
241
+
242
+ ### min()
243
+
244
+ Returns the minimum amount of time spent in the operation.
245
+
246
+ ``` ruby
247
+ meter = Metriks.timer('requests')
248
+ puts "time: #{meter.min} seconds"
249
+ ```
250
+
251
+ ### max()
252
+
253
+ Returns the maximum time spent in the operation.
254
+
255
+ ``` ruby
256
+ meter = Metriks.timer('requests')
257
+ puts "time: #{meter.max} seconds"
258
+ ```
259
+
260
+ ### mean()
261
+
262
+ Returns the mean (average) time spent in the operation.
263
+
264
+ ``` ruby
265
+ meter = Metriks.timer('requests')
266
+ puts "time: #{meter.mean} seconds"
267
+ ```
268
+
269
+ ### stddev()
270
+
271
+ Returns the standard deviation of the mean spent in the operation.
272
+
273
+ ``` ruby
274
+ meter = Metriks.timer('requests')
275
+ puts "time: #{meter.stddev} seconds"
276
+ ```
277
+
278
+
279
+ ## Utilization Timer
280
+
281
+ A specialized `Timer` that calculates the percentage (between `0.0` and `1.0`) of
282
+ wall-clock time that was spent. It includes all of the methods of `Timer`.
283
+
284
+
285
+ ### one_minute_utilization()
286
+
287
+ Returns the one-minute average utilization as a percentage between `0.0` and `1.0`.
288
+
289
+ ``` ruby
290
+ meter = Metriks.utilization_timer('requests')
291
+ puts "utilization: #{meter.one_minute_utilization * 100}%"
292
+ ```
293
+
294
+ ### five_minute_utilization()
295
+
296
+ Returns the five-minute average utilization as a percentage between `0.0` and `1.0`.
297
+
298
+ ``` ruby
299
+ meter = Metriks.utilization_timer('requests')
300
+ puts "utilization: #{meter.five_minute_utilization * 100}%"
301
+ ```
302
+
303
+ ### fifteen_minute_utilization()
304
+
305
+ Returns the fifteen-minute average utilization as a percentage between `0.0` and `1.0`.
306
+
307
+ ``` ruby
308
+ meter = Metriks.utilization_timer('requests')
309
+ puts "utilization: #{meter.fifteen_minute_utilization * 100}%"
310
+ ```
311
+
312
+ ### mean_utilization()
313
+
314
+ Returns the mean (average) utilization as a percentage between `0.0` and `1.0`
315
+ since the process started.
316
+
317
+ ``` ruby
318
+ meter = Metriks.utilization_timer('requests')
319
+ puts "utilization: #{meter.mean_utilization * 100}%"
320
+ ```
321
+
322
+
323
+ # Reporter Overview
324
+
325
+ How to get metrics out of the process.
326
+
327
+ ## Graphite Reporter
328
+
329
+ Sends metrics to Graphite every 60 seconds.
330
+
331
+ ``` ruby
332
+ reporter = Metriks::Reporter::Graphite.new 'localhost', 3004
333
+ reporter.start
334
+ ```
335
+
336
+
337
+ ## Logger Reporter
338
+
339
+ Send metrics to a logger every 60 seconds.
340
+
341
+ ``` ruby
342
+ reporter = Metriks::Reporter::Logger.new(:logger => Logger.new('log/metrics.log'))
343
+ reporter.start
344
+ ```
345
+
346
+
347
+ ## Librato Metrics Reporter
348
+
349
+ Send metrics to Librato Metrics every 60 seconds.
350
+
351
+ ``` ruby
352
+ reporter = Metriks::Reporter::LibratoMetrics.new('email', 'token')
353
+ reporter.start
354
+ ```
355
+
356
+
357
+ ## Proc Title Reporter
358
+
359
+ Provides a simple way to get up-to-date statistics from a process by
360
+ updating the proctitle every 5 seconds (default).
361
+
362
+ ``` ruby
363
+ reporter = Metriks::Reporter::ProcTitle.new :interval => 5
364
+ reporter.add 'reqs', 'sec' do
365
+ Metriks.meter('rack.requests').one_minute_rate
366
+ end
367
+ reporter.start
368
+ ```
369
+
370
+ will display:
371
+
372
+ ```
373
+ 501 17015 26.0 1.9 416976 246956 ? Ss 18:54 11:43 thin reqs: 273.3/sec
374
+ ```
375
+
376
+ # Application Server Configuration
377
+
378
+ Depending on how your application server operates, you may need to configure how reporters are created. Please look at [Troubleshooting](https://github.com/eric/metriks/wiki/Troubleshooting) for more information.
379
+
380
+ # Plans
381
+
382
+ An incomplete list of things I would like to see added:
383
+
384
+ * Rack middleware to measure utilization, throughput and worker time
385
+ * Basic reporters:
386
+ * Rack endpoint returning JSON
387
+ * [Statsd](https://github.com/etsy/statsd) reporter
388
+ * Metaprogramming instrumentation hooks like [Shopify's statsd-instrument](https://github.com/Shopify/statsd-instrument)
389
+
390
+
391
+ # Credits
392
+
393
+ Most of the inspiration for this project comes from Coda Hale's amazing
394
+ [Metrics, Metrics Everywhere][metrics-talk] talk at CodeConf and his sweet
395
+ [Metrics][metrics] Java Library.
396
+
397
+ [metrics-talk]: http://pivotallabs.com/talks/139-metrics-metrics-everywhere
398
+ [metrics]: https://github.com/codahale/metrics
399
+
400
+
401
+ # License
402
+
403
+ Copyright (c) 2012 Eric Lindvall
404
+
405
+ Published under the MIT License, see LICENSE
data/Rakefile ADDED
@@ -0,0 +1,150 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'date'
4
+
5
+ #############################################################################
6
+ #
7
+ # Helper functions
8
+ #
9
+ #############################################################################
10
+
11
+ def name
12
+ @name ||= Dir['*.gemspec'].first.split('.').first
13
+ end
14
+
15
+ def version
16
+ line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
17
+ line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
18
+ end
19
+
20
+ def date
21
+ Date.today.to_s
22
+ end
23
+
24
+ def rubyforge_project
25
+ name
26
+ end
27
+
28
+ def gemspec_file
29
+ "#{name}.gemspec"
30
+ end
31
+
32
+ def gem_file
33
+ "#{name}-#{version}.gem"
34
+ end
35
+
36
+ def replace_header(head, header_name)
37
+ head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
38
+ end
39
+
40
+ #############################################################################
41
+ #
42
+ # Standard tasks
43
+ #
44
+ #############################################################################
45
+
46
+ task :default => :test
47
+
48
+ require 'rake/testtask'
49
+ Rake::TestTask.new(:test) do |test|
50
+ test.libs << 'lib' << 'test'
51
+ test.pattern = 'test/**/*_test.rb'
52
+ test.verbose = true
53
+ end
54
+
55
+ desc "Generate RCov test coverage and open in your browser"
56
+ task :coverage do
57
+ require 'rcov'
58
+ sh "rm -fr coverage"
59
+ sh "rcov test/*_test.rb"
60
+ sh "open coverage/index.html"
61
+ end
62
+
63
+ # require 'rake/rdoctask'
64
+ # Rake::RDocTask.new do |rdoc|
65
+ # rdoc.rdoc_dir = 'rdoc'
66
+ # rdoc.title = "#{name} #{version}"
67
+ # rdoc.rdoc_files.include('README*')
68
+ # rdoc.rdoc_files.include('lib/**/*.rb')
69
+ # end
70
+
71
+ desc "Open an irb session preloaded with this library"
72
+ task :console do
73
+ sh "irb -rubygems -r ./lib/#{name}.rb"
74
+ end
75
+
76
+ #############################################################################
77
+ #
78
+ # Custom tasks (add your own tasks here)
79
+ #
80
+ #############################################################################
81
+
82
+
83
+
84
+ #############################################################################
85
+ #
86
+ # Packaging tasks
87
+ #
88
+ #############################################################################
89
+
90
+ desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
91
+ task :release => :build do
92
+ unless `git branch` =~ /^\* master$/
93
+ puts "You must be on the master branch to release!"
94
+ exit!
95
+ end
96
+ sh "git commit --allow-empty -a -m 'Release #{version}'"
97
+ sh "git tag v#{version}"
98
+ sh "git push origin master"
99
+ sh "git push origin v#{version}"
100
+ sh "gem push pkg/#{name}-#{version}.gem"
101
+ end
102
+
103
+ desc "Build #{gem_file} into the pkg directory"
104
+ task :build => :gemspec do
105
+ sh "mkdir -p pkg"
106
+ sh "gem build #{gemspec_file}"
107
+ sh "mv #{gem_file} pkg"
108
+ end
109
+
110
+ desc "Generate #{gemspec_file}"
111
+ task :gemspec => :validate do
112
+ # read spec file and split out manifest section
113
+ spec = File.read(gemspec_file)
114
+ head, manifest, tail = spec.split(" # = MANIFEST =\n")
115
+
116
+ # replace name version and date
117
+ replace_header(head, :name)
118
+ replace_header(head, :version)
119
+ replace_header(head, :date)
120
+ #comment this out if your rubyforge_project has a different name
121
+ replace_header(head, :rubyforge_project)
122
+
123
+ # determine file list from git ls-files
124
+ files = `git ls-files`.
125
+ split("\n").
126
+ sort.
127
+ reject { |file| file =~ /^\./ }.
128
+ reject { |file| file =~ /^(rdoc|pkg)/ }.
129
+ map { |file| " #{file}" }.
130
+ join("\n")
131
+
132
+ # piece file back together and write
133
+ manifest = " s.files = %w[\n#{files}\n ]\n"
134
+ spec = [head, manifest, tail].join(" # = MANIFEST =\n")
135
+ File.open(gemspec_file, 'w') { |io| io.write(spec) }
136
+ puts "Updated #{gemspec_file}"
137
+ end
138
+
139
+ desc "Validate #{gemspec_file}"
140
+ task :validate do
141
+ libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
142
+ unless libfiles.empty?
143
+ puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
144
+ exit!
145
+ end
146
+ unless Dir['VERSION*'].empty?
147
+ puts "A `VERSION` file at root level violates Gem best practices."
148
+ exit!
149
+ end
150
+ end
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'benchmark'
4
+ require 'metriks'
5
+ require 'rbtree'
6
+ require 'avl_tree'
7
+ require 'red_black_tree'
8
+
9
+ fib_times = ARGV[0] ? ARGV[0].to_i : 10
10
+ iter = ARGV[1] ? ARGV[1].to_i : 100000
11
+
12
+
13
+ class TimerBenchmarker
14
+ attr_reader :iter, :fib_times
15
+
16
+ def initialize(fib_times, iter)
17
+ @fib_times = fib_times
18
+ @iter = iter
19
+ @mapping = { :plain => nil }
20
+ end
21
+
22
+ def measure(key, value)
23
+ @mapping[key] = value
24
+ end
25
+
26
+ def run
27
+ @results = {}
28
+ @mapping.each do |key, timer|
29
+ @results[key] = Benchmark.realtime do
30
+ if timer
31
+ for i in 1..iter
32
+ timer.time do
33
+ fib(fib_times)
34
+ end
35
+ end
36
+ else
37
+ for i in 1..iter
38
+ fib(fib_times)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ report
44
+ end
45
+
46
+ def report
47
+ results = @results.sort_by { |k,v| v }
48
+ results.each_with_index do |(name, time), idx|
49
+ puts "%23s: %f secs %f secs/call" % [
50
+ name, time, time / iter
51
+ ]
52
+
53
+ if idx > 0
54
+ prev_name, prev_time = results[idx - 1]
55
+ puts "#{' ' * 25} - %.1f%% slower than %s (%f secs/call)" % [
56
+ (time - prev_time) / prev_time * 100, prev_name,
57
+ (time - prev_time) / iter
58
+ ]
59
+ end
60
+
61
+ if idx > 1
62
+ plain_name, plain_time = results[0]
63
+ puts "#{' ' * 25} - %.1f%% slower than %s (%f secs/call)" % [
64
+ (time - plain_time) / plain_time * 100, plain_name,
65
+ (time - plain_time) / iter
66
+ ]
67
+ end
68
+ end
69
+ end
70
+
71
+ def fib(n)
72
+ n < 2 ? n : fib(n-1) + fib(n-2)
73
+ end
74
+ end
75
+
76
+ reporter = TimerBenchmarker.new(fib_times, iter)
77
+
78
+ reporter.measure :uniform, Metriks::Timer.new(Metriks::Histogram.new_uniform)
79
+
80
+ reporter.measure :exponential, Metriks::Timer.new(Metriks::ExponentiallyDecayingSample.new(
81
+ Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA, RBTree.new))
82
+
83
+ reporter.measure :exponential_avl, Metriks::Timer.new(Metriks::ExponentiallyDecayingSample.new(
84
+ Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA, AVLTree.new))
85
+
86
+ reporter.measure :exponential_red_black, Metriks::Timer.new(Metriks::ExponentiallyDecayingSample.new(
87
+ Metriks::Histogram::DEFAULT_SAMPLE_SIZE, Metriks::Histogram::DEFAULT_ALPHA, RedBlackTree.new))
88
+
89
+ puts "fib(#{fib_times}): #{iter} iterations"
90
+ puts "-" * 50
91
+
92
+ reporter.run