From 49210c8a49bb85ef2566a24306ef3493fa95aac7 Mon Sep 17 00:00:00 2001 From: Walt Jones Date: Mon, 26 Aug 2024 10:39:51 -0400 Subject: [PATCH] enable Rails 7 error reporter via Rollbar config --- lib/rollbar/configuration.rb | 16 +++++ lib/rollbar/exception_reporter.rb | 3 +- lib/rollbar/plugins/rails.rb | 8 +++ lib/rollbar/plugins/rails/error_subscriber.rb | 9 +++ spec/controllers/home_controller_spec.rb | 65 +++++++++++++++++++ .../app/controllers/home_controller.rb | 16 ++++- spec/dummyapp/config/routes.rb | 2 + 7 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 lib/rollbar/plugins/rails/error_subscriber.rb diff --git a/lib/rollbar/configuration.rb b/lib/rollbar/configuration.rb index 202e8741..de2a881a 100644 --- a/lib/rollbar/configuration.rb +++ b/lib/rollbar/configuration.rb @@ -76,6 +76,7 @@ class Configuration :web_base, :write_to_file attr_reader :before_process, + :enable_rails_error_subscriber, :logger_level, :project_gem_paths, :send_extra_frame_data, @@ -104,6 +105,7 @@ def initialize @disable_core_monkey_patch = false @disable_rack_monkey_patch = false @enable_error_context = true + @enable_rails_error_subscriber = false @dj_threshold = 0 @dj_use_scoped_block = false @async_skip_report_handler = nil @@ -337,6 +339,20 @@ def send_extra_frame_data=(value) @send_extra_frame_data = value end + def enable_rails_error_subscriber=(enable) + return if !defined?(::Rails) || ::Rails.gem_version < ::Gem::Version.new('7.1.0') + + if @enable_rails_error_subscriber && !enable + ::Rails.error.unsubscribe(Rollbar::ErrorSubscriber) + end + + if !@enable_rails_error_subscriber && enable + ::Rails.error.subscribe(Rollbar::ErrorSubscriber.new) + end + + @enable_rails_error_subscriber = enable + end + # allow params to be read like a hash def [](option) send(option) diff --git a/lib/rollbar/exception_reporter.rb b/lib/rollbar/exception_reporter.rb index 1dc73317..18b334f3 100644 --- a/lib/rollbar/exception_reporter.rb +++ b/lib/rollbar/exception_reporter.rb @@ -27,7 +27,8 @@ def report_exception_to_rollbar(env, exception) end def capture_uncaught? - Rollbar.configuration.capture_uncaught != false + Rollbar.configuration.capture_uncaught != false && + !Rollbar.configuration.enable_rails_error_subscriber end def log_exception_message(exception) diff --git a/lib/rollbar/plugins/rails.rb b/lib/rollbar/plugins/rails.rb index 24e787b3..a6cc9e80 100644 --- a/lib/rollbar/plugins/rails.rb +++ b/lib/rollbar/plugins/rails.rb @@ -79,3 +79,11 @@ def secure_headers_middleware? Rollbar::Js::Frameworks::Rails.new.load(self) end end + +Rollbar.plugins.define('rails-error-subscriber') do + dependency { defined?(Rails::VERSION) && Rails::VERSION::MAJOR >= 7 } + + execute! do + require 'rollbar/plugins/rails/error_subscriber' + end +end diff --git a/lib/rollbar/plugins/rails/error_subscriber.rb b/lib/rollbar/plugins/rails/error_subscriber.rb new file mode 100644 index 00000000..b496f213 --- /dev/null +++ b/lib/rollbar/plugins/rails/error_subscriber.rb @@ -0,0 +1,9 @@ +module Rollbar + class ErrorSubscriber + def report(error, handled:, severity:, context:, source: nil) + extra = context.is_a?(Hash) ? context.deep_dup : {} + extra[:custom_data_method_context] = source + Rollbar.log(severity, error, extra) + end + end +end diff --git a/spec/controllers/home_controller_spec.rb b/spec/controllers/home_controller_spec.rb index e9640752..45aafde1 100644 --- a/spec/controllers/home_controller_spec.rb +++ b/spec/controllers/home_controller_spec.rb @@ -297,6 +297,71 @@ def send_req(meth, path, args) end end + describe 'rails error subscriber', :type => 'request' do + let(:notifier) { Rollbar.notifier } + + before do + Rollbar.configure do |config| + config.enable_rails_error_subscriber = true + end + end + + after do + Rollbar.configure do |config| + config.enable_rails_error_subscriber = false + end + end + + context 'when Rails Error Subscriber is enabled', if: ::Rails.gem_version >= ::Gem::Version.new('7.1.0') do + it '`handle` should not raise an error and report a warning via rails error subscriber' do + logger_mock.should_receive(:info).with('[Rollbar] Success').never + + expect(Rollbar).to receive(:log) do |level, _, extra| + expect(extra[:custom_data_method_context]).to be_eql('application') + expect(level.to_s).to be_eql('warning') + end + + get '/handle_rails_error' + end + + it '`report` should raise an error and report an error via rails error subscriber' do + logger_mock.should_receive(:info).with('[Rollbar] Success').never + + expect(Rollbar).to receive(:log) do |level, _, extra| + expect(extra[:custom_data_method_context]).to be_eql('application') + expect(level.to_s).to be_eql('error') + end + + expect do + get '/record_rails_error' + end.to raise_exception(RuntimeError, 'Record Rails error') + end + + it 'uncaught exception should raise an error and report an error via rails error subscriber' do + logger_mock.should_receive(:info).with('[Rollbar] Success').never + + expect(Rollbar).to receive(:log) do |level, _, extra| + expect(extra[:custom_data_method_context]).to be_eql('application.action_dispatch') + expect(level.to_s).to be_eql('error') + end + + expect do + get '/cause_exception' + end.to raise_exception(NameError, 'Uncaught Rails error') + end + end + + context 'when Rails Error Subscriber is enabled in unsupported Rails', if: ::Rails.gem_version < ::Gem::Version.new('7.1.0') do + it 'uncaught exception should raise an error and report via middleware' do + logger_mock.should_receive(:info).with('[Rollbar] Success').once + + expect do + get '/cause_exception' + end.to raise_exception(NameError, 'Uncaught Rails error') + end + end + end + describe 'configuration.locals', :type => 'request', :if => RUBY_VERSION >= '2.3.0' && !(defined?(RUBY_ENGINE) && diff --git a/spec/dummyapp/app/controllers/home_controller.rb b/spec/dummyapp/app/controllers/home_controller.rb index be38840e..5ced1b75 100644 --- a/spec/dummyapp/app/controllers/home_controller.rb +++ b/spec/dummyapp/app/controllers/home_controller.rb @@ -22,8 +22,22 @@ def deprecated_report_exception render :json => {} end + def handle_rails_error + Rails.error.handle do + raise 'Handle Rails error' + end + + render :json => {} + end + + def record_rails_error + Rails.error.record do + raise 'Record Rails error' + end + end + def cause_exception - _foo = bar + raise NameError, 'Uncaught Rails error' end def cause_exception_with_locals diff --git a/spec/dummyapp/config/routes.rb b/spec/dummyapp/config/routes.rb index 9cdb49c6..520942b9 100644 --- a/spec/dummyapp/config/routes.rb +++ b/spec/dummyapp/config/routes.rb @@ -4,6 +4,8 @@ member { post :start_session } end + match '/handle_rails_error' => 'home#handle_rails_error', :via => [:get, :post] + match '/record_rails_error' => 'home#record_rails_error', :via => [:get, :post] match '/cause_exception' => 'home#cause_exception', :via => [:get, :post] match '/cause_exception_with_locals' => 'home#cause_exception_with_locals', :via => [:get, :post]