diff --git a/lib/rollbar/notifier.rb b/lib/rollbar/notifier.rb index c48be05c..9631ffdd 100644 --- a/lib/rollbar/notifier.rb +++ b/lib/rollbar/notifier.rb @@ -211,7 +211,9 @@ def process_item(item) end rescue StandardError => e log_error("[Rollbar] Error processing the item: #{e.class}, #{e.message}. Item: #{item.payload.inspect}") - raise e + raise e unless via_failsafe?(item) + + log_error('[Rollbar] Item has already failed. Not re-raising') end # We will reraise exceptions in this method so async queues @@ -772,5 +774,9 @@ def log_instance_link(data) uuid_url = Util.uuid_rollbar_url(data, configuration) log_info "[Rollbar] Details: #{uuid_url} (only available if report was successful)" end + + def via_failsafe?(item) + item.payload.fetch('data', {}).fetch(:failsafe, false) + end end end diff --git a/spec/dummyapp/app/assets/config/manifest.js b/spec/dummyapp/app/assets/config/manifest.js new file mode 100644 index 00000000..e69de29b diff --git a/spec/rollbar/notifier_spec.rb b/spec/rollbar/notifier_spec.rb index 7e0b9aaf..9c1e77da 100644 --- a/spec/rollbar/notifier_spec.rb +++ b/spec/rollbar/notifier_spec.rb @@ -1,5 +1,6 @@ require 'rollbar' require 'rollbar/notifier' +require 'spec_helper' describe Rollbar::Notifier do describe '#scope' do @@ -41,6 +42,82 @@ end end + describe '#process_item' do + subject(:process_item) { notifier.process_item(item) } + let(:notifier) { described_class.new } + let(:payload) { { :foo => :bar } } + let(:item) { Rollbar::Item.build_with(payload) } + let(:logger) { double(Logger).as_null_object } + + before { notifier.configuration.logger = logger } + + context 'when configured to write' do + before { notifier.configuration.write_to_file = true } + + let(:dummy_file) { double(File).as_null_object } + + it 'writes to the file' do + allow(File).to receive(:open).with(nil, 'a').and_return(dummy_file) + + process_item + + expect(dummy_file).to have_received(:puts).with(payload.to_json) + end + end + + context 'when configured not to write' do + before do + notifier.configuration.write_to_file = false + + allow(File).to receive(:open).with(nil, 'a').and_return(dummy_file) + allow(Net::HTTP).to receive(:new).and_return(dummy_http) + allow(::Rollbar).to receive(:log_error) + end + + let(:dummy_file) { double(File).as_null_object } + let(:dummy_http) { double(Net::HTTP).as_null_object } + + it 'does not write to the file' do + process_item + + expect(dummy_file).not_to have_received(:puts).with(item) + end + + it 'attempts to send via HTTP' do + process_item + + expect(dummy_http).to have_received(:request) + end + + context 'a socket error occurs' do + before { allow(dummy_http).to receive(:request).and_raise(SocketError) } + + it 'passes the message on' do + expect {process_item}.to raise_error(SocketError) + end + + context 'the item has come via failsafe' do + let(:exception) { SocketError.new('original exception') } + let(:payload) { notifier.send_failsafe('the failure', exception) } + + it 'does not pass the message on' do + expect { process_item }.to_not raise_error + end + end + end + end + end + + describe '#send_failsafe' do + subject(:send_failsafe) { described_class.new.send_failsafe(message, exception) } + let(:message) { 'testing failsafe' } + let(:exception) { StandardError.new } + + it 'sets a flag on the payload so we know the payload has come through this way' do + expect(send_failsafe['data']).to include(:failsafe => true) + end + end + if RUBY_PLATFORM == 'java' describe '#extract_arguments' do # See https://docs.oracle.com/javase/8/docs/api/java/lang/Throwable.html