metriks 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'turn'
7
+ 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.
@@ -0,0 +1,333 @@
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
+ Everything is still in flux, so for the time being I have been installing
13
+ the gem from git with bundler. If I get a request, I can definitely start
14
+ releasing a gem.
15
+
16
+ To install, add this to your `Gemfile`:
17
+
18
+ gem 'metriks', :git => 'git://github.com/eric/metriks.git'
19
+
20
+ and re-run `bundle`.
21
+
22
+
23
+ # Metric API Overview
24
+
25
+ ## Counters
26
+
27
+ Basic atomic counter. Used as an underlying metric for many of the other
28
+ more advanced metrics.
29
+
30
+
31
+ ### increment(incr = 1)
32
+
33
+ Increment the counter. Without an argument it will increment by `1`.
34
+
35
+ ``` ruby
36
+ counter = Metriks.counter('calls')
37
+ counter.increment
38
+ ```
39
+
40
+ ### decrement(decr = 1)
41
+
42
+ Decrement the counter. Without an argument it will decrement by `1`.
43
+
44
+ ``` ruby
45
+ counter = Metriks.counter('calls')
46
+ counter.decrement
47
+ ```
48
+
49
+ #### count()
50
+
51
+ Return the current value of the counter.
52
+
53
+ ``` ruby
54
+ counter = Metriks.counter('calls')
55
+ puts "counter: #{counter.count}"
56
+ ```
57
+
58
+ ## Meters
59
+
60
+ A meter that measures the mean throughput and the one-, five-, and
61
+ fifteen-minute exponentially-weighted moving average throughputs.
62
+
63
+ ### mark(val = 1)
64
+
65
+ Record an event with the meter. Without an argument it will record one event.
66
+
67
+ ``` ruby
68
+ meter = Metriks.meter('requests')
69
+ meter.mark
70
+ ```
71
+
72
+ ### count()
73
+
74
+ Returns the total number of events that have been recorded.
75
+
76
+ ``` ruby
77
+ meter = Metriks.meter('requests')
78
+ puts "total: #{meter.count}"
79
+ ```
80
+
81
+ ### one_minute_rate()
82
+
83
+ Returns the one-minute average rate.
84
+
85
+ ``` ruby
86
+ meter = Metriks.meter('requests')
87
+ puts "rate: #{meter.one_minute_rate}/sec"
88
+ ```
89
+
90
+ ### five_minute_rate()
91
+
92
+ Returns the five-minute average rate.
93
+
94
+ ``` ruby
95
+ meter = Metriks.meter('requests')
96
+ puts "rate: #{meter.five_minute_rate}/sec"
97
+ ```
98
+
99
+ ### fifteen_minute_rate()
100
+
101
+ Returns the fifteen-minute average rate.
102
+
103
+ ``` ruby
104
+ meter = Metriks.meter('requests')
105
+ puts "rate: #{meter.fifteen_minute_rate}/sec"
106
+ ```
107
+
108
+ ### mean_rate()
109
+
110
+ Returns the mean (average) rate of the events since the start of the process.
111
+
112
+ ``` ruby
113
+ meter = Metriks.meter('requests')
114
+ puts "rate: #{meter.mean_rate}/sec"
115
+ ```
116
+
117
+ ## Timers
118
+
119
+ A timer that measures the average time as well as throughput metrics via
120
+ a meter.
121
+
122
+ ### update(duration)
123
+
124
+ Records the duration of an operation. This normally wouldn't need to be
125
+ called — the `#time` method is provided to simplify recording a duration.
126
+
127
+ ``` ruby
128
+ timer = Metriks.timer('requests')
129
+ t0 = Time.now
130
+ work
131
+ timer.update(Time.now - t0)
132
+ ```
133
+
134
+ ### time(callable = nil, &block)
135
+
136
+ Measure the amount of time a proc takes to execute. Takes either a block
137
+ or an object responding to `#call` (normally a `proc` or `lambda`).
138
+
139
+ ``` ruby
140
+ timer = Metriks.timer('requests')
141
+ timer.time do
142
+ work
143
+ end
144
+ ```
145
+
146
+ If neither a block or an object is passed to the method, an object that
147
+ responds to `#stop` will be returned. When `#stop` is called, the time
148
+ will be recorded.
149
+
150
+ ``` ruby
151
+ timer = Metriks.timer('requests')
152
+ t = timer.time
153
+ work
154
+ t.stop
155
+ ```
156
+
157
+ ### count()
158
+
159
+ Returns the number of measurements that have been made.
160
+
161
+ ``` ruby
162
+ timer = Metriks.timer('requests')
163
+ puts "calls: #{timer.count}"
164
+ ```
165
+
166
+ ### one_minute_rate()
167
+
168
+ Returns the one-minute average rate.
169
+
170
+ ``` ruby
171
+ meter = Metriks.timer('requests')
172
+ puts "rate: #{meter.one_minute_rate}/sec"
173
+ ```
174
+
175
+ ### five_minute_rate()
176
+
177
+ Returns the five-minute average rate.
178
+
179
+ ``` ruby
180
+ meter = Metriks.timer('requests')
181
+ puts "rate: #{meter.five_minute_rate}/sec"
182
+ ```
183
+
184
+ ### fifteen_minute_rate()
185
+
186
+ Returns the fifteen-minute average rate.
187
+
188
+ ``` ruby
189
+ meter = Metriks.timer('requests')
190
+ puts "rate: #{meter.fifteen_minute_rate}/sec"
191
+ ```
192
+
193
+ ### mean_rate()
194
+
195
+ Returns the mean (average) rate of the events since the start of the process.
196
+
197
+ ``` ruby
198
+ meter = Metriks.timer('requests')
199
+ puts "rate: #{meter.mean_rate}/sec"
200
+ ```
201
+
202
+ ### min()
203
+
204
+ Returns the minimum amount of time spent in the operation.
205
+
206
+ ``` ruby
207
+ meter = Metriks.timer('requests')
208
+ puts "time: #{meter.min} seconds"
209
+ ```
210
+
211
+ ### max()
212
+
213
+ Returns the maximum time spent in the operation.
214
+
215
+ ``` ruby
216
+ meter = Metriks.timer('requests')
217
+ puts "time: #{meter.max} seconds"
218
+ ```
219
+
220
+ ### mean()
221
+
222
+ Returns the mean (average) time spent in the operation.
223
+
224
+ ``` ruby
225
+ meter = Metriks.timer('requests')
226
+ puts "time: #{meter.mean} seconds"
227
+ ```
228
+
229
+ ### stddev()
230
+
231
+ Returns the standard deviation of the mean spent in the operation.
232
+
233
+ ``` ruby
234
+ meter = Metriks.timer('requests')
235
+ puts "time: #{meter.stddev} seconds"
236
+ ```
237
+
238
+
239
+ ## Utilization Timer
240
+
241
+ A specialized `Timer` that calculates the percentage (between `0.0` and `1.0`) of
242
+ wall-clock time that was spent. It includes all of the methods of `Timer`.
243
+
244
+
245
+ ### one_minute_utilization()
246
+
247
+ Returns the one-minute average utilization as a percentage between `0.0` and `1.0`.
248
+
249
+ ``` ruby
250
+ meter = Metriks.utilization_timer('requests')
251
+ puts "utilization: #{meter.one_minute_utilization * 100}%"
252
+ ```
253
+
254
+ ### five_minute_utilization()
255
+
256
+ Returns the five-minute average utilization as a percentage between `0.0` and `1.0`.
257
+
258
+ ``` ruby
259
+ meter = Metriks.utilization_timer('requests')
260
+ puts "utilization: #{meter.five_minute_utilization * 100}%"
261
+ ```
262
+
263
+ ### fifteen_minute_utilization()
264
+
265
+ Returns the fifteen-minute average utilization as a percentage between `0.0` and `1.0`.
266
+
267
+ ``` ruby
268
+ meter = Metriks.utilization_timer('requests')
269
+ puts "utilization: #{meter.fifteen_minute_utilization * 100}%"
270
+ ```
271
+
272
+ ### mean_utilization()
273
+
274
+ Returns the mean (average) utilization as a percentage between `0.0` and `1.0`
275
+ since the process started.
276
+
277
+ ``` ruby
278
+ meter = Metriks.utilization_timer('requests')
279
+ puts "utilization: #{meter.mean_utilization * 100}%"
280
+ ```
281
+
282
+
283
+ # Reporter Overview
284
+
285
+ How to get metrics out of the process.
286
+
287
+ ## Proc Title Reporter
288
+
289
+ Provides a simple way to get up-to-date statistics from a process by
290
+ updating the proctitle every 5 seconds (default).
291
+
292
+ ```ruby
293
+ reporter = Metriks::Reporter::ProcTitle.new :interval => 5
294
+
295
+ reporter.add 'reqs', 'sec' do
296
+ Metriks.meter('rack.requests').one_minute_rate
297
+ end
298
+
299
+ reporter.start
300
+ ```
301
+
302
+ will display:
303
+
304
+ ```
305
+ 501 17015 26.0 1.9 416976 246956 ? Ss 18:54 11:43 thin reqs: 273.3/sec
306
+ ```
307
+
308
+
309
+ # Plans
310
+
311
+ An incomplete list of things I would like to see added:
312
+
313
+ * Rack middleware to measure utilization, throughput and worker time
314
+ * Basic reporters:
315
+ * Rack endpoint returning JSON
316
+ * Logger reporter to output metrics on a time interval
317
+ * [Statsd](https://github.com/etsy/statsd) reporter
318
+ * [Librato Metrics](http://metrics.librato.com) reporter
319
+ * Metaprogramming instrumentation hooks like [Shopify's statsd-instrument](https://github.com/Shopify/statsd-instrument)
320
+
321
+
322
+ # Credits
323
+
324
+ Most of the inspiration for this project comes from the amazing talk that
325
+ Code Hale gave at CodeConf and his sweet
326
+ [Metrics](https://github.com/codahale/metrics) Java library.
327
+
328
+
329
+ # License
330
+
331
+ Copyright (c) 2012 Eric Lindvall
332
+
333
+ Published under the MIT License, see LICENSE
@@ -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