class Raygun::Apm::Rails::Middleware

Constants

POSTGRESQLSTRING

Public Class Methods

new(app) click to toggle source
# File lib/raygun/apm/rails/middleware.rb, line 6
def initialize(app)
  @app = app
  @mutex = Mutex.new
  @tracer = nil
end

Private Class Methods

finalize(tracer) click to toggle source
# File lib/raygun/apm/rails/middleware.rb, line 200
def self.finalize(tracer)
  proc {tracer.process_ended}
end

Public Instance Methods

call(env) click to toggle source
# File lib/raygun/apm/rails/middleware.rb, line 12
def call(env)
  @status, @headers, @response = instrument(env)
  [@status, @headers, @response]
end

Private Instance Methods

Ruby_APM_profiler_trace() { || ... } click to toggle source
# File lib/raygun/apm/rails/middleware.rb, line 185
def Ruby_APM_profiler_trace
  yield
end
Ruby_APM_request_error() { || ... } click to toggle source
# File lib/raygun/apm/rails/middleware.rb, line 189
def Ruby_APM_request_error
  yield
end
crash_report_exception(exception, env = {}) click to toggle source
# File lib/raygun/apm/rails/middleware.rb, line 193
def crash_report_exception(exception, env = {})
  if Raygun::Apm::Rails.raygun4ruby?
    env.merge!(correlation_id: exception.instance_variable_get(:@__raygun_correlation_id))
    Raygun.track_exception(exception, env)
  end
end
exceptional_http_in_handler(env) click to toggle source

For middleware chain halts that does not trigger 'process_action.action_controller'

# File lib/raygun/apm/rails/middleware.rb, line 125
def exceptional_http_in_handler(env)
  req = Rack::Request.new env
  event = http_in_event
  event[:pid] = Process.pid
  event[:url] = req.url
  event[:verb] = req.request_method
  event[:status] = 500
  event[:duration] = @tracer.now - @request_started
  event[:timestamp] = @tracer.now
  event[:tid] = @tracer.get_thread_id(Thread.current)
  @tracer.emit(event)
rescue => e
  warn "[Raygun APM] error reporting exceptional HTTP IN event"
  raygun_shutdown_handler(e)
end
get_sql_provider_name(config) click to toggle source

This function adjusts the provider name for PostgreSQL to Postgres because all other Raygun profilers use Postgres and this will keep the UI consistent across supported languages

# File lib/raygun/apm/rails/middleware.rb, line 174
def get_sql_provider_name(config)
  if config[:adapter] == POSTGRESQLSTRING 
    return "postgres"
  end
  config[:adapter]
end
http_in_event() click to toggle source
# File lib/raygun/apm/rails/middleware.rb, line 141
def http_in_event
  @http_in_event ||= Raygun::Apm::Event::HttpIn.new
end
http_in_handler(args) click to toggle source
# File lib/raygun/apm/rails/middleware.rb, line 89
def http_in_handler(args)
  notification = ActiveSupport::Notifications::Event.new *args
  if notification.payload[:exception]
    return
  end
  if headers = notification.payload[:headers]
    req = Rack::Request.new headers.env
    url = req.url
    verb = req.request_method
  else
    url = notification.payload[:path]
    verb = notification.payload[:method].to_s.upcase
  end
  event = http_in_event
  event[:pid] = Process.pid
  event[:url] = url
  event[:verb] = verb
  event[:status] = http_in_status(notification)
  # XXX constant milliseconds to microseconds
  event[:duration] = notification.duration * 1000
  event[:timestamp] = @tracer.now
  event[:tid] = @tracer.get_thread_id(Thread.current)
  @tracer.emit(event)
rescue => e
  warn "[Raygun APM] error reporting HTTP IN event"
  raygun_shutdown_handler(e)
end
http_in_status(notification) click to toggle source
# File lib/raygun/apm/rails/middleware.rb, line 117
def http_in_status(notification)
  notification.payload[:status] || notification.payload[:headers]["action_controller.instance"].status
rescue
  warn "[Raygun APM] error inferring HTTP status, fallback to 200"
  200
end
init_tracer() click to toggle source
# File lib/raygun/apm/rails/middleware.rb, line 55
def init_tracer
  tracer = Raygun::Apm::Tracer.new
  tracer.udp_sink!
  ObjectSpace.define_finalizer(self, self.class.finalize(tracer))

  ActiveSupport::Notifications.unsubscribe(@http_in_subscriber) if @http_in_subscriber
  @http_in_subscriber = ActiveSupport::Notifications.subscribe('process_action.action_controller') do |*args|
    http_in_handler(args) if @tracer
  end

  ActiveSupport::Notifications.unsubscribe(@sql_subscriber) if @sql_subscriber
  @sql_subscriber = ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
    sql_handler(args) if @tracer
  end

  GC.stress = true if ENV['RAYGUN_STRESS_GC']
  Raygun::Apm::Tracer.instance = tracer
  # If any fatal errors on init, shutdown the tracer
rescue Raygun::Apm::FatalError => e
  raygun_shutdown_handler(e)
end
instrument(env) click to toggle source
# File lib/raygun/apm/rails/middleware.rb, line 19
def instrument(env)
  # Ignore asset requests
  if (env['REQUEST_PATH'] || env['REQUEST_URI']).match(/^\/assets\//)
    return @app.call(env)
  end
  @mutex.synchronize do
    # Can be nil if we had a fatal error
    @tracer = Raygun::Apm::Tracer.instance || init_tracer
  end
  if @tracer
    # For the exceptional HTTP IN handler
    @request_started = @tracer.now
    @tracer.start_trace
  end
  exception = nil
  res = nil
  Ruby_APM_profiler_trace do
    begin
      res = @app.call(env)
    rescue => e
      crash_report_exception(e, env)
      exceptional_http_in_handler(env) if @tracer
      exception = e
    end
  end
  # Can be nil if we had a fatal error
  if @tracer
    @tracer.diagnostics if ENV['PROTON_DIAGNOSTICS']
    @tracer.end_trace
  end
  raise exception if exception
  res
rescue Raygun::Apm::FatalError => e
  raygun_shutdown_handler(e)
end
raygun_shutdown_handler(exception) click to toggle source
# File lib/raygun/apm/rails/middleware.rb, line 77
def raygun_shutdown_handler(exception)
  warn "[Raygun APM] shutting down due to error - #{exception.message} #{exception.backtrace.join("\n")}",
  # Kill extended event subcriptions
  ActiveSupport::Notifications.unsubscribe(@http_in_subscriber)
  ActiveSupport::Notifications.unsubscribe(@sql_subscriber)
  warn "[Raygun APM] notification hooks unsubscribed"
  @tracer.end_trace
  # Let the GC clean up the sink thread through the finalizer below
  @tracer = Raygun::Apm::Tracer.instance = nil
  raise(exception) unless (Raygun::Apm::FatalError === exception)
end
sql_event() click to toggle source
# File lib/raygun/apm/rails/middleware.rb, line 181
def sql_event
  @sql_event ||= Raygun::Apm::Event::Sql.new
end
sql_handler(args) click to toggle source
# File lib/raygun/apm/rails/middleware.rb, line 145
def sql_handler(args)
  notification = ActiveSupport::Notifications::Event.new *args
  connection = if notification.payload[:connection]
    notification.payload[:connection]
  else
    ObjectSpace._id2ref(notification.payload[:connection_id])
  end
  event = sql_event
  event[:pid] = Process.pid
  event[:query] = notification.payload[:sql]
  
  # XXX this is hacky
  if config = connection.instance_variable_get('@config')
    event[:provider] = get_sql_provider_name(config)
    event[:host] = config[:host]
    event[:database] = config[:database]
  end
  
  # XXX constant milliseconds to microseconds
  event[:duration] = notification.duration * 1000
  event[:timestamp] = notification.time.to_f * 1000000
  event[:tid] = @tracer.get_thread_id(Thread.current)
  @tracer.emit(event)
rescue => e
  warn "[Raygun APM] error reporting SQL event"
  raygun_shutdown_handler(e)
end