diff --git a/lib/lograge.rb b/lib/lograge.rb index b7376f5d..3874a83f 100644 --- a/lib/lograge.rb +++ b/lib/lograge.rb @@ -117,6 +117,7 @@ def unsubscribe(component, subscriber) end def setup(app) + require 'lograge/rails_ext/action_dispatch/debug_exceptions' self.application = app disable_rack_cache_verbose_output keep_original_rails_log diff --git a/lib/lograge/log_subscriber.rb b/lib/lograge/log_subscriber.rb index ebb5b702..2688c463 100644 --- a/lib/lograge/log_subscriber.rb +++ b/lib/lograge/log_subscriber.rb @@ -15,6 +15,7 @@ def process_action(event) formatted_message = Lograge.formatter.call(data) logger.send(Lograge.log_level, formatted_message) end + alias process_exception process_action def redirect_to(event) RequestStore.store[:lograge_location] = event.payload[:location] diff --git a/lib/lograge/rails_ext/action_dispatch/debug_exceptions.rb b/lib/lograge/rails_ext/action_dispatch/debug_exceptions.rb new file mode 100644 index 00000000..1b9e32e9 --- /dev/null +++ b/lib/lograge/rails_ext/action_dispatch/debug_exceptions.rb @@ -0,0 +1,17 @@ +require 'action_dispatch/middleware/debug_exceptions' + +module Lograge + module DebugExceptions + def log_error(request, wrapper) + payload = { + path: request.fullpath, + method: request.method, + format: request.format.ref, + exception: [wrapper.exception.class.name, wrapper.exception.message] + } + ActiveSupport::Notifications.instrument 'process_exception.action_controller', payload + super(request, wrapper) if Lograge.lograge_config.keep_original_rails_log + end + end + ActionDispatch::DebugExceptions.prepend DebugExceptions +end diff --git a/spec/lograge_spec.rb b/spec/lograge_spec.rb index ce3afeb5..ad4d7906 100644 --- a/spec/lograge_spec.rb +++ b/spec/lograge_spec.rb @@ -212,4 +212,62 @@ def current_user_id end end end + + describe 'handling exceptions' do + let(:app_config) do + double(config: + ActiveSupport::OrderedOptions.new.tap do |config| + config.action_dispatch = double(rack_cache: false) + config.lograge = ActiveSupport::OrderedOptions.new + end) + end + let(:debug_exceptions) do + # Workaround `undefined method 'blamed_files'` bug + require 'active_support/dependencies' + # Workaround `undefined method 'with_indifferent_access'` bug: + # https://github.com/rails/rails/issues/33634 + require 'active_support/core_ext/hash/indifferent_access' + ActionDispatch::DebugExceptions.new(->(_) { raise }) + end + let(:output) { StringIO.new } + let(:logger) { Logger.new(output) } + let(:env) do + Rack::MockRequest.env_for( + '', + 'action_dispatch.show_detailed_exceptions' => true, + 'action_dispatch.logger' => logger + ) + end + + before do + Lograge.setup(app_config) + Lograge.logger = logger + end + + it 'adds formatted exception log' do + debug_exceptions.call(env) + expect(output.string).to match(/status=500 error='RuntimeError: '/) + end + + it 'removes original exception log' do + debug_exceptions.call(env) + expect(output.string).not_to match(/FATAL -- : RuntimeError/) + end + + context 'when keep_original_rails_log is true' do + before do + app_config.config.lograge.keep_original_rails_log = true + end + + it 'adds formatted exception log' do + debug_exceptions.call(env) + expect(output.string).to match(/status=500 error='RuntimeError: '/) + end + + it 'keeps original exception log' do + debug_exceptions.call(env) + expect(output.string).to match(/FATAL -- : RuntimeError/) + end + end + end end