StatsD

and rails

Ed Robinson

Background

Reevoomark

  • 9 Application Servers
  • Frontend servers in 5 AWS regions
  • Performance Is Realy Important

What is StatsD?

  • A network daemon written in node.js
  • listens for statistics over UDP
  • Fire and Forget
  • Pluggable Backend services including Graphite

The Stack

Installing StatsD

brew install node
npm install statsd

In production we use upstart to keep it running

Development Configuration

{
  port: 8125,
  debug: true,
  backends: [ "/usr/local/share/npm/lib/node_modules/statsd/backends/console.js" ]
}

Getting Stuff Into StatsD

  • Need to fire off UDP requests
  • There are lots of libraries that make this simple

In Ruby

statsd-ruby
require 'statsd'
$statsd = Statsd.new 'localhost', 8125

$statsd.increment 'hits'

$statsd.timing 'request', 320

$statsd.time('slow') do
  sleep 5
end

In Rails

37signals.com/svn/posts/3091-pssst-your-rails-application-has-a-secret-to-tell-you
ActiveSupport::Notifications.subscribe /process_action.action_controller/ do |*args|.
  event = ActiveSupport::Notifications::Event.new(*args)
  controller = event.payload[:controller]
  action = event.payload[:action]
  format = event.payload[:format] || "all".
  format = "all" if format == "*/*".
  status = event.payload[:status]
  key = "#{controller}.#{action}.#{format}".
  ActiveSupport::Notifications.instrument :performance, 
                                          :action => :timing, 
                                          :measurement => "#{key}.total_duration", 
                                          :value => event.duration
  ActiveSupport::Notifications.instrument :performance, 
                                          :action => :timing, 
                                          :measurement => "#{key}.db_time", 
                                          :value => event.payload[:db_runtime]
  ActiveSupport::Notifications.instrument :performance, 
                                          :action => :timing, 
                                          :measurement => "#{key}.view_time", 
                                          :value => event.payload[:view_runtime]
  ActiveSupport::Notifications.instrument :performance, 
                                          :measurement => "#{key}.status.#{status}".
end
module StatsdIntegration
  def send_event_to_statsd(name, payload)
    action = payload[:action] || :increment
    measurement = payload[:measurement]
    value = payload[:value]
    key_name = "#{name.to_s.capitalize}.#{measurement}"
    batch = Statsd::Batch.new(Reevoomark3.statsd)
    batch.__send__ action.to_s, "production.#{key_name}", (value || 1)
    batch.__send__ action.to_s, "#{nodename}.#{key_name}", (value || 1)
    batch.flush
  end

  def nodename
    @@nodename ||= Sys::Uname.nodename.gsub(/\./,'-')
  end
end
ActiveSupport::Notifications.subscribe /performance/ do |name, start, finish, id, payload|.
  StatsdIntegration.send_event_to_statsd(name, payload)
end

Profit

Its Over!