# 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](https://papertrailapp.com/).


# 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`:

``` ruby
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`.

``` ruby
  counter = Metriks.counter('calls')
  counter.increment
```

### decrement(decr = 1)

Decrement the counter. Without an argument it will decrement by `1`.

``` ruby
  counter = Metriks.counter('calls')
  counter.decrement
```

#### count()

Return the current value of the counter.

``` ruby
  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.

``` ruby
  # 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.

``` ruby
  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.

``` ruby
  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.

``` ruby
  meter = Metriks.meter('requests')
  meter.mark
```

### count()

Returns the total number of events that have been recorded.

``` ruby
  meter = Metriks.meter('requests')
  puts "total: #{meter.count}"
```

### one_minute_rate()

Returns the one-minute average rate.

``` ruby
  meter = Metriks.meter('requests')
  puts "rate: #{meter.one_minute_rate}/sec"
```

### five_minute_rate()

Returns the five-minute average rate.

``` ruby
  meter = Metriks.meter('requests')
  puts "rate: #{meter.five_minute_rate}/sec"
```

### fifteen_minute_rate()

Returns the fifteen-minute average rate.

``` ruby
  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.

``` ruby
  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.

``` ruby
  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`).

``` ruby
  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.

``` ruby
  timer = Metriks.timer('requests')
  t = timer.time
  work
  t.stop
```

### count()

Returns the number of measurements that have been made.

``` ruby
  timer = Metriks.timer('requests')
  puts "calls: #{timer.count}"
```

### one_minute_rate()

Returns the one-minute average rate.

``` ruby
  meter = Metriks.timer('requests')
  puts "rate: #{meter.one_minute_rate}/sec"
```

### five_minute_rate()

Returns the five-minute average rate.

``` ruby
  meter = Metriks.timer('requests')
  puts "rate: #{meter.five_minute_rate}/sec"
```

### fifteen_minute_rate()

Returns the fifteen-minute average rate.

``` ruby
  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.

``` ruby
  meter = Metriks.timer('requests')
  puts "rate: #{meter.mean_rate}/sec"
```

### min()

Returns the minimum amount of time spent in the operation.

``` ruby
  meter = Metriks.timer('requests')
  puts "time: #{meter.min} seconds"
```

### max()

Returns the maximum time spent in the operation.

``` ruby
  meter = Metriks.timer('requests')
  puts "time: #{meter.max} seconds"
```

### mean()

Returns the mean (average) time spent in the operation.

``` ruby
  meter = Metriks.timer('requests')
  puts "time: #{meter.mean} seconds"
```

### stddev()

Returns the standard deviation of the mean spent in the operation.

``` ruby
  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`.

``` ruby
  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`.

``` ruby
  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`.

``` ruby
  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.

``` ruby
  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.

``` ruby
  reporter = Metriks::Reporter::Graphite.new 'localhost', 3004
  reporter.start
```


## Logger Reporter

Send metrics to a logger every 60 seconds.

``` ruby
  reporter = Metriks::Reporter::Logger.new(:logger => Logger.new('log/metrics.log'))
  reporter.start
```


## Librato Metrics Reporter

Send metrics to Librato Metrics every 60 seconds.

``` ruby
  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).

``` ruby
  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
```

## Sematext Metrics Reporter

[metriks-sematext](https://github.com/sematext/metriks-sematext) gem provides reporter for sending metrics to [SPM](http://sematext.com/spm/index.html).

# Application Server Configuration

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.

# 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](https://github.com/etsy/statsd) reporter
* Metaprogramming instrumentation hooks like [Shopify's statsd-instrument](https://github.com/Shopify/statsd-instrument)


# Credits

Most of the inspiration for this project comes from Coda Hale's amazing
[Metrics, Metrics Everywhere][metrics-talk] talk at CodeConf and his sweet
[Metrics][metrics] Java Library.

[metrics-talk]: http://pivotallabs.com/talks/139-metrics-metrics-everywhere
[metrics]: https://github.com/codahale/metrics


# License

Copyright (c) 2012 Eric Lindvall

Published under the MIT License, see LICENSE
