Knowledge Base: Ruby Customization Guide

Creating Recipes

All instrumentation in Dash is contained within recipes. A recipe is a container, identified by a name and URL, for collecting similar metrics. For example, the ActiveRecord metrics provided by FiveRuns, has the name ActiveRecord and the URL http://dash.fiveruns.com. You can add your own ActiveRecord metrics, so long as they use a different URL.

Using a recipe involves two steps:

  1. Defining the recipe with Ruby
  2. Adding the recipe to the Dash configuration for your application, which may be done differently depending on if it's a Ruby daemon, Rails application, Merb application, Sinatra application, or other.

We explain both of these in detail below.

Defining the Recipe

Recipes might be defined:

  • By application developers, to monitor their own application.
  • By library and framework developers, who package the recipe along with their code for use by application developers.

The first step towards an instrumented recipe is deciding on a name for the recipe. You'll want to associate this a URL, probably your own. Armed with a name and URL, you can create your recipe.

The location of this file does not matter, so put it wherever works best for you. In Rails applications, we recommend you put custom Dash recipes in config/initializers.

Here's a basic, empty recipe:

Fiveruns::Dash.register_recipe :example, :url => 'http://example.com' do |recipe|
  # Your metrics are defined here
end

Now it's time to add metrics.

Adding a Metric

Once you've defined your recipe, it's time to instrument your application. Let's suppose you've got an instance method process on a class AwesomeTransaction. You want to track the number of times you call that method.

class AwesomeTransaction
  def process
    puts "Awesome."
  end
end

To do so, you'd add a line like so in your recipe:

Fiveruns::Dash.register_recipe :example, :url => 'http://example.com' do |recipe|
  recipe.counter :transactions, 
                 :incremented_by => 'AwesomeTransaction#process'
end

This will create a metric named transactions that is incremented every time AwesomeTransaction#process is called.

In addition to counters, you can define metrics that track time, absolute values and utilization. See below for more detailed information on creating metrics.

Metric Types

Dash provides some standard metrics that you will probably use in every application you instrument. Should these metrics not quite fit what you want to do, you can define your own as well. Let's look at the standard metrics, going from most specific to most general.

Note: Dash uses a convention for naming methods that much of the Ruby community has adopted from Smalltalk. If we wish to refer to an instance method bar on the class Foo, we write this as Foo#bar - that is, we use a # to indicate the method is on an instance of Foo. If we want to address a method on the class, we use a .; thus Gizmo.urf! indicates a call to the class method urf on Gizmo.

Counters

Counters are used to track an ever increasing number. You'll probably use these for tracking data like requests, transactions, purchases and visits.

There are a couple different ways to use counters. The simplest way is to specify which methods cause the counter to increment. Suppose we're instrumenting this class:

class Salesman

  def sell_cars
    puts "Close the deal Gil!"
  end

  def sell_houses
    puts "If you lived here, you'd be home by now!"
  end

end

If we want to track all sales, we would instrument Salesman like so:

Fiveruns::Dash.register_recipe :salesman, :url => 'http://example.com' do |recipe|
  recipe.counter :sales, 
                 :incremented_by => ['Salesman#sell_car', 
                                     'Salesman#sell_house']
end

The sales metric would then reflect the number calls to both sell_car and sell_house.

Timers

Adding a timer on a method lets you know how much time you're spending inside that method. Often you'd like to know what your bottlenecks are. Putting a timer on methods that process or fetch data can show you to start your optimization efforts.

Suppose we're instrumenting this class:

class Doubler

  def self.double(n)
    new(n).double
  end

  def initialize(n)
    @num = n
  end

  def double
    @num.modulo(2) == 0 ? even : odd
  end

  def even
    @num * 2
  end

  def odd
    sleep 3
    @num * 2
  end

end

(1..10).map { |n| Doubler.double(n) }

We can track how much time we're spending in this class with the following recipe:

Fiveruns::Dash.register_recipe :doubler, :url => 'http://example.com' do |recipe|
  recipe.time :double_time, 
              :method => 'Doubler#double'
end

With that recipe, you'd find that indeed you're spending a lot of time in Doubler#double. Digging further, you'd want to instrument both Doubler#even and Doubler#odd. Adding a timer on multiple methods is easy:

Fiveruns::Dash.register_recipe :doubler, :url => 'http://example.com' do |recipe|
  recipe.time :double_time, 
              :methods => ['Doubler#even', 'Doubler#odd']
end

When you are instrumenting multiple methods for time, you might run into the situation where one method calls the other method. This will cause Dash to count the time twice when calculating the total time spent in the methods. To fix this, add the :reentrant => true option to the time metric and Dash will only track the outermost time in the call stack.

Absolute values

Instrumenting absolute values allows us to track the change in some value over time. Suppose we're handing out beta invites on our hot new service and want to track how many remain. We can look up all the remaining invitations like so:

Invitations.unclaimed.length # => 10

To track this number in Dash, we'd just put that code inside an absolute metric:

Fiveruns::Dash.register_recipe :invitations, :url => 'http://example.com' do |recipe|
  recipe.absolute :invitations, :unit => 'invitations' do
    Invitations.unclaimed.length
  end
end

The value returned by the block passed to absolute will be recorded for the invitations metric each time data is collected. The units option allows you to set the label that will appear next to the number in Dash reports online.

Utilization

Utilization metrics allow you to track how much of a finite resource is in use. You might use this for the percent of queue workers that are available or to track the ratio of free users to paying users.

Let's suppose you're tracking the percentage of users in your forum who have sent at least ten messages. If you had an ActiveRecord model like this:

class User < ActiveRecord::Base

  has_many :messages

  def frequent_messager?
    messages.length > 10
  end

end

We can extract a percentage metric like so:

Fiveruns::Dash.register_recipe :users, :url => 'http://example.com' do |recipe|
  recipe.percentage :frequent_messagers, 'Frequent messagers' do
    (User.count / User.all.select(&:frequent_messager?)) * 100.00
  end
end

Distributing recipes

Recipes can be packaged in any .rb file and are loaded simply by using require.

Adding a Recipe to a Configuration

How you add your custom recipes differs for each application configuration.

Rails Applications

In the initializer file you added when setting up your Rails application, you just need to add your recipe to the configuration, which is yielded to a block passed to Fiveruns::Dash::Rails.start:

Fiveruns::Dash::Rails.start(:production => 'YOUR-APP-TOKEN-HERE') do |config|
  # Note we can require and add pre-defined recipes, perhaps from other gems
  require 'my_recipe'
  config.add_recipe :recipe_name, :url => 'http://example.com/recipe/url'

  # Or we can define application-specific recipes inline
  config.add_recipe :inline_recipe, :url => 'http://example.com/recipe/url' do |recipe|
    recipe.absolute :active_users do
      User.active.count
    end
  end
end

Restart and, make sure you add your metric to a report so you can see it.

Merb Applications

In the environment file you modified when setting up your Merb application, you just need to require & add your recipe to the configuration settings:

Merb::Config.use do |c|
  # ...
  c[:dash][:token] = 'THE-APP-TOKEN-YOU-SET'
  c[:dash][:recipes] << [:recipe_name, {:url => 'http://example.com/recipe/url'}]
  # ...
end

Restart and, make sure you add your metric to a report so you can see it.

Sinatra Applications

After setting up your Sinatra application, you just need to add your recipe to the configuration-- which is yielded to a block passed to Fiveruns::Dash::Sinatra.start:

Fiveruns::Dash::Sinatra.start('YOUR-APP-TOKEN') do |config|
  config.add_recipe :recipe_name, :url => 'http://example.com/recipe/url'
end

Restart and, make sure you add your metric to a report so you can see it.

Other Ruby daemons

TODO