Skip to content

Commit

Permalink
Merge pull request #1225 from airbrake/railtie-refactor
Browse files Browse the repository at this point in the history
Refactor railties so that every railtie is a separate class
  • Loading branch information
kyrylo committed May 12, 2022
2 parents 827a760 + 6278acb commit a1d79ed
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 103 deletions.
109 changes: 6 additions & 103 deletions lib/airbrake/rails/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,10 @@ module Rails
# This railtie works for any Rails application that supports railties (Rails
# 3.2+ apps). It makes Airbrake Ruby work with Rails and report errors
# occurring in the application automatically.
#
# rubocop:disable Metrics/BlockLength
class Railtie < ::Rails::Railtie
initializer('airbrake.middleware') do |app|
# Since Rails 3.2 the ActionDispatch::DebugExceptions middleware is
# responsible for logging exceptions and showing a debugging page in
# case the request is local. We want to insert our middleware after
# DebugExceptions, so we don't notify Airbrake about local requests.

if ::Rails.version.to_i >= 5
# Avoid the warning about deprecated strings.
# Insert after DebugExceptions, since ConnectionManagement doesn't
# exist in Rails 5 anymore.
app.config.middleware.insert_after(
ActionDispatch::DebugExceptions,
Airbrake::Rack::Middleware,
)
elsif defined?(::ActiveRecord::ConnectionAdapters::ConnectionManagement)
# Insert after ConnectionManagement to avoid DB connection leakage:
# https://github.com/airbrake/airbrake/pull/568
app.config.middleware.insert_after(
::ActiveRecord::ConnectionAdapters::ConnectionManagement,
'Airbrake::Rack::Middleware',
)
else
# Insert after DebugExceptions for apps without ActiveRecord.
app.config.middleware.insert_after(
ActionDispatch::DebugExceptions,
'Airbrake::Rack::Middleware',
)
end
require 'airbrake/rails/railties/middleware_tie'
Railties::MiddlewareTie.new(app).call
end

rake_tasks do
Expand All @@ -47,82 +20,13 @@ class Railtie < ::Rails::Railtie
end

initializer('airbrake.action_controller') do
ActiveSupport.on_load(:action_controller, run_once: true) do
# Patches ActionController with methods that allow us to retrieve
# interesting request data. Appends that information to notices.
require 'airbrake/rails/action_controller'
include Airbrake::Rails::ActionController

# Cache route information for the duration of the request.
require 'airbrake/rails/action_controller_route_subscriber'
ActiveSupport::Notifications.subscribe(
'start_processing.action_controller',
Airbrake::Rails::ActionControllerRouteSubscriber.new,
)

# Send route stats.
require 'airbrake/rails/action_controller_notify_subscriber'
ActiveSupport::Notifications.subscribe(
'process_action.action_controller',
Airbrake::Rails::ActionControllerNotifySubscriber.new(::Rails.version),
)

# Send performance breakdown: where a request spends its time.
require 'airbrake/rails/action_controller_performance_breakdown_subscriber'
ActiveSupport::Notifications.subscribe(
'process_action.action_controller',
Airbrake::Rails::ActionControllerPerformanceBreakdownSubscriber.new,
)

require 'airbrake/rails/net_http' if defined?(Net) && defined?(Net::HTTP)
require 'airbrake/rails/curb' if defined?(Curl) && defined?(Curl::CURB_VERSION)
require 'airbrake/rails/http' if defined?(HTTP) && defined?(HTTP::Client)
require 'airbrake/rails/http_client' if defined?(HTTPClient)
require 'airbrake/rails/typhoeus' if defined?(Typhoeus)

if defined?(Excon)
require 'airbrake/rails/excon_subscriber'
ActiveSupport::Notifications.subscribe(/excon/, Airbrake::Rails::Excon.new)
::Excon.defaults[:instrumentor] = ActiveSupport::Notifications
end
end
require 'airbrake/rails/railties/action_controller_tie'
Railties::ActionControllerTie.new.call
end

initializer('airbrake.active_record') do
ActiveSupport.on_load(:active_record, run_once: true) do
# Reports exceptions occurring in some bugged ActiveRecord callbacks.
# Applicable only to the versions of Rails lower than 4.2.
if defined?(::Rails) &&
Gem::Version.new(::Rails.version) <= Gem::Version.new('4.2')
require 'airbrake/rails/active_record'
include Airbrake::Rails::ActiveRecord
end

if defined?(ActiveRecord)
# Send SQL queries.
require 'airbrake/rails/active_record_subscriber'
ActiveSupport::Notifications.subscribe(
'sql.active_record', Airbrake::Rails::ActiveRecordSubscriber.new
)

# Filter out parameters from SQL body.
if ::ActiveRecord::Base.respond_to?(:connection_db_config)
# Rails 6.1+ deprecates "connection_config" in favor of
# "connection_db_config", so we need an updated call.
Airbrake.add_performance_filter(
Airbrake::Filters::SqlFilter.new(
::ActiveRecord::Base.connection_db_config.configuration_hash[:adapter],
),
)
else
Airbrake.add_performance_filter(
Airbrake::Filters::SqlFilter.new(
::ActiveRecord::Base.connection_config[:adapter],
),
)
end
end
end
require 'airbrake/rails/railties/active_record_tie'
Railties::ActiveRecordTie.new.call
end

initializer('airbrake.active_job') do
Expand All @@ -146,6 +50,5 @@ class Railtie < ::Rails::Railtie
end
end
end
# rubocop:enable Metrics/BlockLength
end
end
92 changes: 92 additions & 0 deletions lib/airbrake/rails/railties/action_controller_tie.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# frozen_string_literal: true

require 'airbrake/rails/action_controller'
require 'airbrake/rails/action_controller_route_subscriber'
require 'airbrake/rails/action_controller_notify_subscriber'
require 'airbrake/rails/action_controller_performance_breakdown_subscriber'

module Airbrake
module Rails
module Railties
# Ties Airbrake APM (routes) and HTTP clients with Rails.
#
# @api private
# @since v13.0.1
class ActionControllerTie
def initialize
@route_subscriber = Airbrake::Rails::ActionControllerRouteSubscriber.new
@notify_subscriber = Airbrake::Rails::ActionControllerNotifySubscriber.new(
::Rails.version,
)
@performance_breakdown_subscriber =
Airbrake::Rails::ActionControllerPerformanceBreakdownSubscriber.new
end

def call
ActiveSupport.on_load(:action_controller, run_once: true, yield: self) do
# Patches ActionController with methods that allow us to retrieve
# interesting request data. Appends that information to notices.
::ActionController::Base.include(Airbrake::Rails::ActionController)

tie_routes_apm
tie_http_integrations
end
end

private

def tie_routes_apm
[
# Cache route information for the duration of the request.
['start_processing.action_controller', @route_subscriber],

# Send route stats.
['process_action.action_controller', @notify_subscriber],

# Send performance breakdown: where a request spends its time.
['process_action.action_controller', @performance_breakdown_subscriber],
].each do |(event, callback)|
ActiveSupport::Notifications.subscribe(event, callback)
end
end

def tie_http_integrations
tie_net_http
tie_curl
tie_http
tie_http_client
tie_typhoeus
tie_excon
end

def tie_net_http
require 'airbrake/rails/net_http' if defined?(Net) && defined?(Net::HTTP)
end

def tie_curl
require 'airbrake/rails/curb' if defined?(Curl) && defined?(Curl::CURB_VERSION)
end

def tie_http
require 'airbrake/rails/http' if defined?(HTTP) && defined?(HTTP::Client)
end

def tie_http_client
require 'airbrake/rails/http_client' if defined?(HTTPClient)
end

def tie_typhoeus
require 'airbrake/rails/typhoeus' if defined?(Typhoeus)
end

def tie_excon
return unless defined?(Excon)

require 'airbrake/rails/excon_subscriber'
ActiveSupport::Notifications.subscribe(/excon/, Airbrake::Rails::Excon.new)
::Excon.defaults[:instrumentor] = ActiveSupport::Notifications
end
end
end
end
end
65 changes: 65 additions & 0 deletions lib/airbrake/rails/railties/active_record_tie.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# frozen_string_literal: true

require 'airbrake/rails/active_record'
require 'airbrake/rails/active_record_subscriber'

module Airbrake
module Rails
module Railties
# Ties Airbrake APM (queries) with Rails.
#
# @api private
# @since v13.0.1
class ActiveRecordTie
def initialize
@active_record_subscriber = Airbrake::Rails::ActiveRecordSubscriber.new
end

def call
ActiveSupport.on_load(:active_record, run_once: true, yield: self) do
tie_activerecord_callback_fix
tie_activerecord_apm
end
end

private

def tie_activerecord_callback_fix
# Reports exceptions occurring in some bugged ActiveRecord callbacks.
# Applicable only to the versions of Rails lower than 4.2.
return unless defined?(::Rails)
return if Gem::Version.new(::Rails.version) > Gem::Version.new('4.2')

ActiveRecord::Base.include(Airbrake::Rails::ActiveRecord)
end

def tie_activerecord_apm
return unless defined?(ActiveRecord)

# Send SQL queries.
ActiveSupport::Notifications.subscribe(
'sql.active_record',
@active_record_subscriber,
)

# Filter out parameters from SQL body.
if ::ActiveRecord::Base.respond_to?(:connection_db_config)
# Rails 6.1+ deprecates "connection_config" in favor of
# "connection_db_config", so we need an updated call.
Airbrake.add_performance_filter(
Airbrake::Filters::SqlFilter.new(
::ActiveRecord::Base.connection_db_config.configuration_hash[:adapter],
),
)
else
Airbrake.add_performance_filter(
Airbrake::Filters::SqlFilter.new(
::ActiveRecord::Base.connection_config[:adapter],
),
)
end
end
end
end
end
end
62 changes: 62 additions & 0 deletions lib/airbrake/rails/railties/middleware_tie.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# frozen_string_literal: true

module Airbrake
module Rails
module Railties
# Ties Airbrake Rails Middleware with Rails (error sending).
#
# Since Rails 3.2 the ActionDispatch::DebugExceptions middleware is
# responsible for logging exceptions and showing a debugging page in case
# the request is local. We want to insert our middleware after
# DebugExceptions, so we don't notify Airbrake about local requests.
#
# @api private
# @since v13.0.1
class MiddlewareTie
def initialize(app)
@app = app
@middleware = app.config.middleware
end

def call
return tie_rails_5_or_above if ::Rails.version.to_i >= 5

if defined?(::ActiveRecord::ConnectionAdapters::ConnectionManagement)
return tie_rails_4_or_below_with_active_record
end

tie_rails_4_or_below_with_active_record
end

private

# Avoid the warning about deprecated strings.
# Insert after DebugExceptions, since ConnectionManagement doesn't
# exist in Rails 5 anymore.
def tie_rails_5_or_above
@middleware.insert_after(
ActionDispatch::DebugExceptions,
Airbrake::Rack::Middleware,
)
end

# Insert after ConnectionManagement to avoid DB connection leakage:
# https://github.com/airbrake/airbrake/pull/568
def tie_rails_4_or_below_with_active_record
@middleware.insert_after(
::ActiveRecord::ConnectionAdapters::ConnectionManagement,
'Airbrake::Rack::Middleware',
)
end

# Insert after DebugExceptions for apps without ActiveRecord.
def tie_rails_4_or_below_without_active_record
@middleware.insert_after(
ActionDispatch::DebugExceptions,
'Airbrake::Rack::Middleware',
)
end
end
end
end
end

0 comments on commit a1d79ed

Please sign in to comment.