class AppsignalExtensions::Middleware
Used to open an Appsignal transaction, but to let the callee close it when it is done. The standard Rack middleware for Appsignal closes the transaction as soon as the response triplet gets returned, we need to keep the transaction open as long as the response is being read.
Public Class Methods
new(app)
click to toggle source
Creates a new Appsignal middleware handler with the given Rack app as a callee
@param app the Rack app
# File lib/appsignal_extensions/middleware.rb, line 64 def initialize(app) @app = app end
Public Instance Methods
call(env)
click to toggle source
Calls the application, captures errors, sets up wrappers and so forth
@param env the Rack env @return [Array] the Rack response triplet from upstream
# File lib/appsignal_extensions/middleware.rb, line 72 def call(env) request = ::Rack::Request.new(env) env['action_dispatch.request_id'] ||= SecureRandom.uuid if Appsignal.active? call_with_appsignal(env, request) else call_with_null_transaction(env, request) end end
Private Instance Methods
call_and_capture(env, transaction, request)
click to toggle source
# File lib/appsignal_extensions/middleware.rb, line 84 def call_and_capture(env, transaction, request) env['appsignal.transaction'] = transaction app_name = @app.is_a?(Module) ? @app.to_s : @app.class.to_s # Set the class name properly transaction.set_action('%s#%s' % [app_name, 'call']) transaction.set_metadata('path', request.path) transaction.set_metadata('method', request.request_method) transaction.set_http_or_background_queue_start s, h, b = @app.call(env) # If the app we called wants to close the transaction on it's own, return the response. This # is useful if the app will clean up or close the transaction within an async.callback block, # or within the long response body, or within a hijack proc. return [s, h, b] if h.delete('appsignal.suspend') # If the app didn't ask for the explicit suspend, Wrap the response in a self-closing wrapper # so that the transaction is closed once the response is read in full. This wrapper only works # with response bodies that support #each(). closing_wrapper = TransactionClosingBody.new(b, transaction) [s, h, closing_wrapper] rescue Exception => e # If the raise happens immediately (not in the response read cycle) # set the error and close the transaction so that the data gets sent # to Appsignal right away, and ensure it gets closed transaction.set_error(e) transaction.close raise e end
call_with_appsignal(env, request)
click to toggle source
# File lib/appsignal_extensions/middleware.rb, line 117 def call_with_appsignal(env, request) bare_transaction = Appsignal::Transaction.create( env.fetch('action_dispatch.request_id'), Appsignal::Transaction::HTTP_REQUEST, request ) # Let the app do something to the appsignal transaction if it wants to # Instrument a `process_action`, to set params/action name transaction = Close.new(bare_transaction) status, headers, body = Appsignal.instrument('process_action.rack') do call_and_capture(env, transaction, request) end [status, headers, body] end
call_with_null_transaction(env, request)
click to toggle source
# File lib/appsignal_extensions/middleware.rb, line 112 def call_with_null_transaction(env, request) # Supply the app with a null transaction call_and_capture(env, NullTransaction.new, request) end