Vitals

Build Status Coverage Status

Vitals is your one stop shop for metrics in Ruby and Ruby Web and API frameworks. It currently support Rails, Rack (Sinatra and any Rack-supported frameworks), and Grape.

Installation

Add this line to your application's Gemfile:

gem 'vitals'

And then execute:

$ bundle

Or install it yourself as:

$ gem install vitals

Usage

Rails or Grape

Make an initializers/vitals.rb initializer, and configure Vitals as you'd like:

require 'vitals'
Vitals.configure! do |c|
  c.facility = 'my_service'
  c.reporter = Vitals::Reporters::StatsdReporter.new(host: 'statsd-host', port: 8125)
end

#
# Rails
#
require 'vitals/integrations/notifications/action_controller'
Vitals::Integrations::Notifications::ActionController.subscribe!

# if you also want ActiveJob metrics
require 'vitals/integrations/notifications/action_job'
Vitals::Integrations::Notifications::ActiveJob.subscribe!


#
# Grape
#
require 'vitals/integrations/notifications/grape'
Vitals::Integrations::Notifications::Grape.subscribe!

You can shortcircuit the notifications configuration by using subscribe! right after configuring:

require 'vitals'
Vitals.configure! do |c|
  c.facility = 'my_service'
  c.reporter = Vitals::Reporters::StatsdReporter.new(host: 'statsd-host', port: 8125)
end
# Subscribe to Rails' controllers, background jobs, and grape's API controllers
Vitals.subscribe!(:action_controller, :active_job, :grape)

Rack

You can use the Requests middleware for pure Rack apps or Sinatra, Rails, and Grape instead of, or in addition to, using the notification based instrumentation. Here is a sample Sinatra app:

require 'vitals'
require 'vitals/integrations/rack/requests'

class SinatraTestAPI < Sinatra::Base
  use Vitals::Integrations::Rack::Requests

  get '/foo/bar/baz' do
    sleep 0.1
    "hello get"
  end

  post '/foo/bar/:name' do
    sleep 0.1
    "hello post"
  end
end

Ruby

You can emit metrics from anywhere:

Vitals.inc('my_metric')

Vitals.count('my_metric', 17)

Vitals.gauge('my_metric', 42)

Vitals.timing('my_metric', 500) # milliseconds

Vitals.time('my_metric'){
 # so something slow
}

# Use a dot to logically separate segments

Vitals.timing('category.my_metric', 500) # milliseconds

Configuration Options

The Vitals API is extensible. It should resemble the standard Logger look and feel, and it revolves around 3 concepts:

  1. Reporter - the thing that takes your metrics and flushes them to a metrics agent. Use StatsdReporter in production and ConsoleReporter in development. ConsoleReporter will spit out metrics to stdout as they come. You can also wire them both with MultiReporter. Check the specs for how to do that.
  2. Format - takes the contextual information (host, service, environment) and your metric, and formats them in an order that makes sense for working with Graphite. You have the ProductionFormat, HostLastFormat and NoHostFormat.
  3. Integrations - integrations hook things that we're able to instrument with Vitals. Check integrations for more.

Here's what's available to you at configuration time:

Vitals.configure! do |c|
  # Set your service name (default: 'default')
  c.facility = 'my_service'

  # Set environment (default: taken from RACK_ENV)
  # c.environment = 'env'

  # Set a host (default: taken from hostname)
  # c.host = 'foohost'

  # Use a specific reporter (default: InmemReporter)
  # c.reporter = Vitals::Reporters::ConsoleReporter.new

  # Use a different format perhaps? (default: ProductionFormat)
  # c.format = Vitals::Formats::HostLastFormat

  # Use a different metric path separator, control how hierarcies are generated (default: '.')
  # c.path_sep = '__'
end

Metric Hierarchies

If you leave the default metric path separator, it will be a dot. This means Graphite will parse this and generate a branch in the tree when ever it sees a new segment, so:

requests.api.status.registration.get.200.host-123

Will be created from a GET /api/status/registration

On the other hand, if you choose a non-dot separator, like underscore, then:

requests.api_status_registration.get.200.host-123

Will be generated from a GET /api/status/registration.

You can pick either of those and the trade-offs are:

  • Nested hierarchy (using a dot): fine-grained control over grouping your metrics efficiently. If you want to pick and choose a metric, and be able to group based on topics efficiently. You cannot group cross-segments of a path in graphite.

  • Flat hierarchy (using anything other than a dot, like an underscore): grouping over segments can be glob-like, so quick wins like "graph all errors" can be had like this: api.*.500.*. However, every time you want to group metrics, you will need to find the appropriate glob pattern, which will taxi Graphite for a huge amounts of metrics.

The default separator is a dot, which makes everything hierarchical.

Development

Tests and benchmarks should be run with at least Ruby 2.1 (because of memory profiling API)

$ bundle install && rake spec && rake bench

You can use the following to test various web frameworks using their own integration testing stacks:

$ rake multiverse

Contributing

Fork, implement, add tests, pull request, get my everlasting thanks and a respectable place here :).

Copyright

Copyright (c) 2011-2016 Dotan Nahum @jondot. See LICENSE for further details.