class Librato::Rack

Middleware for rack applications. Installs tracking hearbeat for metric submission and tracks performance metrics.

@example A basic rack app

require 'rack'
require 'librato-rack'

app = Rack::Builder.app do
  use Librato::Rack
  run lambda { |env| [200, {"Content-Type" => 'text/html'}, ["Hello!"]] }
end

@example Using a custom config object

config = Librato::Rack::Configuration.new
config.user = 'myuser@mysite.com'
config.token = 'mytoken'
…more configuration

use Librato::Rack, :config => config
run MyApp

Constants

RECORD_RACK_BODY
RECORD_RACK_METHOD_BODY
RECORD_RACK_STATUS_BODY
VERSION

Attributes

config[R]
tracker[R]

Public Class Methods

new(app, options={}) click to toggle source
# File lib/librato/rack.rb, line 60
def initialize(app, options={})
  @app = app
  @config = options.fetch(:config, Configuration.new)
  @tracker = @config.tracker || Tracker.new(@config)
  Librato.register_tracker(@tracker) # create global reference

  build_record_request_metrics_method
  build_record_header_metrics_method
  build_record_exception_method
end

Public Instance Methods

call(env) click to toggle source
# File lib/librato/rack.rb, line 71
def call(env)
  check_log_output(env) unless @log_target
  @tracker.check_worker
  record_header_metrics(env)
  response, duration = process_request(env)
  record_request_metrics(response.first, env["REQUEST_METHOD"], duration)
  response
end

Private Instance Methods

build_record_exception_method() click to toggle source
# File lib/librato/rack.rb, line 171
def build_record_exception_method
  if tracker.suite_enabled?(:rack)
    define_singleton_method(:record_exception) do |exception|
      return if config.disable_rack_metrics
      tracker.increment 'rack.request.exceptions'
    end
  else
    define_singleton_method(:record_exception) do |exception|
      # no-op
    end
  end
end
build_record_header_metrics_method() click to toggle source

Dynamically construct :record_header_metrics method based on configured metric suites

# File lib/librato/rack.rb, line 146
def build_record_header_metrics_method
  if tracker.suite_enabled?(:rack)
    define_singleton_method(:record_header_metrics) do |env|
      queue_start = env['HTTP_X_REQUEST_START'] || env['HTTP_X_QUEUE_START']
      if queue_start
        queue_start = queue_start.to_s.sub('t=', '').sub('.', '')
        case queue_start.length
        when 16 # microseconds
          wait = ((Time.now.to_f * 1000000).to_i - queue_start.to_i) / 1000.0
          wait = 0 if wait < 0 # make up for potential time drift between the routing server and the application server
          tracker.timing 'rack.request.queue.time', wait, percentile: 95
        when 13 # milliseconds
          wait = (Time.now.to_f * 1000).to_i - queue_start.to_i
          wait = 0 if wait < 0 # make up for potential time drift between the routing server and the application server
          tracker.timing 'rack.request.queue.time', wait, percentile: 95
        end
      end
    end
  else
    define_singleton_method(:record_header_metrics) do |env|
      # no-op
    end
  end
end
build_record_request_metrics_method() click to toggle source

Dynamically construct :record_request_metrics method based on configured metric suites

# File lib/librato/rack.rb, line 117
def build_record_request_metrics_method
  body = "def record_request_metrics(status, http_method, duration)\n"
  body << "return if config.disable_rack_metrics\n"

  unless config.instance_of?(Librato::Rack::Configuration::SuitesNone)
    body << "tracker.group 'rack.request' do |group|\n"

    if tracker.suite_enabled?(:rack)
      body << RECORD_RACK_BODY
    end

    if tracker.suite_enabled?(:rack_status)
      body << RECORD_RACK_STATUS_BODY
    end

    if tracker.suite_enabled?(:rack_method)
      body << RECORD_RACK_METHOD_BODY
    end

    body << "end\n"
  end

  body << "end\n"

  instance_eval(body)
end
check_log_output(env) click to toggle source

this generally will only get called on the first request it figures out the environment-appropriate logging outlet and notifies config and tracker about it

# File lib/librato/rack.rb, line 85
def check_log_output(env)
  return if @log_target
  if in_heroku_env?
    tracker.on_heroku = true
    default = ::Logger.new($stdout)
  else
    default = env['rack.errors'] || $stderr
  end
  @tracker.update_log_target(config.log_target ||= default)
  @log_target = config.log_target
end
in_heroku_env?() click to toggle source
# File lib/librato/rack.rb, line 97
def in_heroku_env?
  # don't have any custom http vars anymore, check if hostname is UUID
  Socket.gethostname =~ /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/i
end
process_request(env) click to toggle source
# File lib/librato/rack.rb, line 102
def process_request(env)
  time = Time.now
  begin
    response = @app.call(env)
  rescue Exception => e
    record_exception(e)
    raise
  end
  duration = (Time.now - time) * 1000.0
  [response, duration]
end