Class: Raygun::Apm::Rails::Middleware

Inherits:
Object
  • Object
show all
Defined in:
lib/raygun/apm/rails/middleware.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ Middleware

Returns a new instance of Middleware.



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/raygun/apm/rails/middleware.rb', line 5

def initialize(app)
  @app = app
  @tracer = Raygun::Apm::Tracer.new
  @tracer.udp_sink!
  @tracer.process_started
  ObjectSpace.define_finalizer(self, self.class.finalize(@tracer))
  
  @http_in_subscriber = ActiveSupport::Notifications.subscribe('process_action.action_controller') do |*args|
    http_in_handler(args)
  end
  @sql_subscriber = ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
    sql_handler(args)
  end
  
  # If any fatal errors on init, shutdown the tracer
rescue Raygun::Apm::FatalError => e
  raygun_shutdown_handler(e)
end

Class Method Details

.finalize(tracer) ⇒ Object



100
101
102
# File 'lib/raygun/apm/rails/middleware.rb', line 100

def self.finalize(tracer)
  proc {tracer.process_ended}
end

Instance Method Details

#call(env) ⇒ Object



24
25
26
27
# File 'lib/raygun/apm/rails/middleware.rb', line 24

def call(env)
  @status, @headers, @response = instrument(env)
  [@status, @headers, @response]
end

#http_in_handler(args) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/raygun/apm/rails/middleware.rb', line 52

def http_in_handler(args)
  notification = ActiveSupport::Notifications::Event.new *args
  if notification.payload[:exception]
    # XXX notify?
    return
  end
  req = Rack::Request.new notification.payload[:headers].env
  event = Raygun::Apm::Event::HttpIn.new
  event[:url] = req.url
  event[:verb] = req.request_method
  event[:status] = notification.payload[:status]
  # XXX constant milliseconds to microseconds
  event[:duration] = notification.duration * 1000
  event[:timestamp] = Time.now.to_f*1000000
  event[:pid] = Process.pid
  event[:tid] = @tracer.get_thread_id(Thread.current)
  @tracer.emit(event)
rescue => e
  # XXX report
end

#instrument(env) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
# File 'lib/raygun/apm/rails/middleware.rb', line 29

def instrument(env)
  res = nil
  # Can be nil if we had a fatal error
  @tracer.start_trace if @tracer
  res = @app.call(env)
  # Can be nil if we had a fatal error
  @tracer.end_trace if @tracer
  res
rescue Raygun::APM::FatalError => e
  raygun_shutdown_handler(e)
end

#raygun_shutdown_handler(exception) ⇒ Object



41
42
43
44
45
46
47
48
49
50
# File 'lib/raygun/apm/rails/middleware.rb', line 41

def raygun_shutdown_handler(exception)
  warn "Raygun APM shutting down due to error - %s", e.message
  # Kill extended event subcriptions
  ActiveSupport::Notifications.unsubscribe(@http_in_subscriber)
  ActiveSupport::Notifications.unsubscribe(@sql_subscriber)
  # Shutdown the tracepoint if enabled to reduce any overhead and stall emission
  @tracer.tracepoint.stop if @tracer.tracepoint.enabled?
  # Let the GC clean up the sink thread through the finalizer below
  @tracer = nil
end

#sql_handler(args) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/raygun/apm/rails/middleware.rb', line 73

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 = Raygun::Apm::Event::Sql.new
  event[:query] = notification.payload[:sql]
  
  # XXX this is hacky
  if config = connection.instance_variable_get('@config')
    event[:provider] = config[:adapter]
    event[:host] = config[:host]
    event[:database] = config[:database]
  end
  
  # XXX constant milliseconds to microseconds
  event[:duration] = notification.duration * 1000
  event[:timestamp] = Time.now.to_f*1000000
  event[:pid] = Process.pid
  event[:tid] = @tracer.get_thread_id(Thread.current)
  @tracer.emit(event)
rescue => e
  # XXX report
end