Metriks Client
This is an experiment in making a threadsafe, low impact library to measure aspects of your ruby.
The library is very much a work-in-progress. It is being developed as I find needs while developing Papertrail.
Installing
The API is still in flux, but you can add this to your project by installing the gem.
To install, add this to your Gemfile
:
gem 'metriks'
and re-run bundle
.
Metric API Overview
Counters
Basic atomic counter. Used as an underlying metric for many of the other more advanced metrics.
increment(incr = 1)
Increment the counter. Without an argument it will increment by 1
.
counter = Metriks.counter('calls')
counter.increment
decrement(decr = 1)
Decrement the counter. Without an argument it will decrement by 1
.
counter = Metriks.counter('calls')
counter.decrement
count()
Return the current value of the counter.
counter = Metriks.counter('calls')
puts "counter: #{counter.count}"
Gauges
A gauge is an instantaneous measurement of a value.
It takes a callback to measure the value in form of a block or a callable object.
WARNING: The code in the callback is executed every time the #value
method is called on the gauge. Most of the time this will be done by a
metriks reporter that is running in a separate thread.
# Callback as block
gauge = Metriks.gauge('queue.size') { queue.size }
# Callback as object responding to #call
callable = proc { queue.size }
gauge = Metriks.gauge('queue.size', callable)
set(val)
Set the current value.
gauge = Metriks.gauge('queue_size')
gauge.set(queue.size)
value()
Returns the value returned by the callback (if one is defined), returns the
value set via #set
(or the default of 0) otherwise.
gauge = Metriks.gauge('queue_size')
puts "queue size: #{gauge.value}"
Meters
A meter that measures the mean throughput and the one-, five-, and fifteen-minute exponentially-weighted moving average throughputs.
mark(val = 1)
Record an event with the meter. Without an argument it will record one event.
meter = Metriks.meter('requests')
meter.mark
count()
Returns the total number of events that have been recorded.
meter = Metriks.meter('requests')
puts "total: #{meter.count}"
one_minute_rate()
Returns the one-minute average rate.
meter = Metriks.meter('requests')
puts "rate: #{meter.one_minute_rate}/sec"
five_minute_rate()
Returns the five-minute average rate.
meter = Metriks.meter('requests')
puts "rate: #{meter.five_minute_rate}/sec"
fifteen_minute_rate()
Returns the fifteen-minute average rate.
meter = Metriks.meter('requests')
puts "rate: #{meter.fifteen_minute_rate}/sec"
mean_rate()
Returns the mean (average) rate of the events since the start of the process.
meter = Metriks.meter('requests')
puts "rate: #{meter.mean_rate}/sec"
Timers
A timer that measures the average time as well as throughput metrics via a meter.
update(duration)
Records the duration of an operation. This normally wouldn't need to be
called — the #time
method is provided to simplify recording a duration.
timer = Metriks.timer('requests')
t0 = Time.now
work
timer.update(Time.now - t0)
time(callable = nil, &block)
Measure the amount of time a proc takes to execute. Takes either a block
or an object responding to #call
(normally a proc
or lambda
).
timer = Metriks.timer('requests')
timer.time do
work
end
If neither a block or an object is passed to the method, an object that
responds to #stop
will be returned. When #stop
is called, the time
will be recorded.
timer = Metriks.timer('requests')
t = timer.time
work
t.stop
count()
Returns the number of measurements that have been made.
timer = Metriks.timer('requests')
puts "calls: #{timer.count}"
one_minute_rate()
Returns the one-minute average rate.
meter = Metriks.timer('requests')
puts "rate: #{meter.one_minute_rate}/sec"
five_minute_rate()
Returns the five-minute average rate.
meter = Metriks.timer('requests')
puts "rate: #{meter.five_minute_rate}/sec"
fifteen_minute_rate()
Returns the fifteen-minute average rate.
meter = Metriks.timer('requests')
puts "rate: #{meter.fifteen_minute_rate}/sec"
mean_rate()
Returns the mean (average) rate of the events since the start of the process.
meter = Metriks.timer('requests')
puts "rate: #{meter.mean_rate}/sec"
min()
Returns the minimum amount of time spent in the operation.
meter = Metriks.timer('requests')
puts "time: #{meter.min} seconds"
max()
Returns the maximum time spent in the operation.
meter = Metriks.timer('requests')
puts "time: #{meter.max} seconds"
mean()
Returns the mean (average) time spent in the operation.
meter = Metriks.timer('requests')
puts "time: #{meter.mean} seconds"
stddev()
Returns the standard deviation of the mean spent in the operation.
meter = Metriks.timer('requests')
puts "time: #{meter.stddev} seconds"
Utilization Timer
A specialized Timer
that calculates the percentage (between 0.0
and 1.0
) of
wall-clock time that was spent. It includes all of the methods of Timer
.
one_minute_utilization()
Returns the one-minute average utilization as a percentage between 0.0
and 1.0
.
meter = Metriks.utilization_timer('requests')
puts "utilization: #{meter.one_minute_utilization * 100}%"
five_minute_utilization()
Returns the five-minute average utilization as a percentage between 0.0
and 1.0
.
meter = Metriks.utilization_timer('requests')
puts "utilization: #{meter.five_minute_utilization * 100}%"
fifteen_minute_utilization()
Returns the fifteen-minute average utilization as a percentage between 0.0
and 1.0
.
meter = Metriks.utilization_timer('requests')
puts "utilization: #{meter.fifteen_minute_utilization * 100}%"
mean_utilization()
Returns the mean (average) utilization as a percentage between 0.0
and 1.0
since the process started.
meter = Metriks.utilization_timer('requests')
puts "utilization: #{meter.mean_utilization * 100}%"
Reporter Overview
How to get metrics out of the process.
Graphite Reporter
Sends metrics to Graphite every 60 seconds.
reporter = Metriks::Reporter::Graphite.new 'localhost', 3004
reporter.start
Logger Reporter
Send metrics to a logger every 60 seconds.
reporter = Metriks::Reporter::Logger.new(:logger => Logger.new('log/metrics.log'))
reporter.start
Librato Metrics Reporter
Send metrics to Librato Metrics every 60 seconds.
reporter = Metriks::Reporter::LibratoMetrics.new('email', 'token')
reporter.start
Proc Title Reporter
Provides a simple way to get up-to-date statistics from a process by updating the proctitle every 5 seconds (default).
reporter = Metriks::Reporter::ProcTitle.new :interval => 5
reporter.add 'reqs', 'sec' do
Metriks.meter('rack.requests').one_minute_rate
end
reporter.start
will display:
501 17015 26.0 1.9 416976 246956 ? Ss 18:54 11:43 thin reqs: 273.3/sec
Application Server Configuration
Depending on how your application server operates, you may need to configure how reporters are created. Please look at Troubleshooting for more information.
Plans
An incomplete list of things I would like to see added:
- Rack middleware to measure utilization, throughput and worker time
- Basic reporters:
- Rack endpoint returning JSON
- Statsd reporter
- Metaprogramming instrumentation hooks like Shopify's statsd-instrument
Credits
Most of the inspiration for this project comes from Coda Hale's amazing Metrics, Metrics Everywhere talk at CodeConf and his sweet Metrics Java Library.
License
Copyright (c) 2012 Eric Lindvall
Published under the MIT License, see LICENSE