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:
- Defining the recipe with Ruby
- 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