From 293199601341dc7c7bfe866047afd9cd3fd69f7f Mon Sep 17 00:00:00 2001 From: Jon de Andres Date: Tue, 30 Sep 2014 01:22:19 +0200 Subject: [PATCH] Support nested exceptions for Ruby 2.1. Fixes #118. --- lib/rollbar.rb | 47 +++++++++++++++++++++++++-------- spec/requests/home_spec.rb | 12 +++++---- spec/rollbar_spec.rb | 47 ++++++++++++++++++++++++++++++++- spec/support/cause_exception.rb | 1 + 4 files changed, 90 insertions(+), 17 deletions(-) create mode 100644 spec/support/cause_exception.rb diff --git a/lib/rollbar.rb b/lib/rollbar.rb index b2d68c23..c2fea020 100644 --- a/lib/rollbar.rb +++ b/lib/rollbar.rb @@ -307,6 +307,37 @@ def exception_data(exception, force_level = nil) data[:level] = force_level if force_level + traces = trace_chain(exception) + + if traces.size > 1 + body = { + :trace_chain => traces + } + elsif traces.size == 1 + body = { + :trace => traces[0] + } + end + + data[:body] = body + + data[:server] = server_data + + data + end + + def trace_chain(exception) + traces = [trace_data(exception)] + + while exception.respond_to?(:cause) && (cause = exception.cause) + traces << trace_data(cause) + exception = cause + end + + traces + end + + def trace_data(exception) # parse backtrace if exception.backtrace.respond_to?( :map ) frames = exception.backtrace.map { |frame| @@ -324,19 +355,13 @@ def exception_data(exception, force_level = nil) frames = [] end - data[:body] = { - :trace => { - :frames => frames, - :exception => { - :class => exception.class.name, - :message => exception.message - } + { + :frames => frames, + :exception => { + :class => exception.class.name, + :message => exception.message } } - - data[:server] = server_data - - data end def logger diff --git a/spec/requests/home_spec.rb b/spec/requests/home_spec.rb index 8528f344..8996d75e 100644 --- a/spec/requests/home_spec.rb +++ b/spec/requests/home_spec.rb @@ -22,7 +22,7 @@ expect{ get 'current_user', nil, :cookie => '8%B' }.to raise_exception Rollbar.last_report.should_not be_nil - + exception_info = Rollbar.last_report[:body][:trace][:exception] exception_info[:class].should == 'ArgumentError' exception_info[:message].should == 'invalid %-encoding (8%B)' @@ -44,11 +44,13 @@ end it "should report uncaught exceptions" do - expect{ get 'current_user' }.to raise_exception + expect { get 'current_user' }.to raise_exception + + body = Rollbar.last_report[:body] + trace = body[:trace] && body[:trace] || body[:trace_chain][0] - exception_info = Rollbar.last_report[:body][:trace][:exception] - exception_info[:class].should == 'NoMethodError' - # exception_info[:message].should == 'undefined method `-\' for "1":String' + trace[:exception][:class].should == 'NoMethodError' + trace[:exception][:message].should == 'undefined method `-\' for "1":String' end end end diff --git a/spec/rollbar_spec.rb b/spec/rollbar_spec.rb index 0b120dfb..0df3dcb5 100644 --- a/spec/rollbar_spec.rb +++ b/spec/rollbar_spec.rb @@ -592,7 +592,7 @@ def backtrace end end - context 'exception_data' do + describe '.exception_data' do before(:each) do configure begin @@ -647,6 +647,51 @@ def backtrace end end + context 'with nested exceptions' do + let(:crashing_code) do + proc do + begin + begin + fail CauseException.new('the cause') + rescue + fail StandardError.new('the error') + end + rescue => e + e + end + end + end + let(:rescued_exception) { crashing_code.call } + + if Exception.instance_methods.include?(:cause) + it 'sends the two exceptions in the trace_chain attribute' do + data = Rollbar.send(:exception_data, rescued_exception) + body = data[:body] + + body[:trace].should be_nil + body[:trace_chain].should be_kind_of(Array) + + chain = body[:trace_chain] + chain[0][:exception][:class].should match(/StandardError/) + chain[0][:exception][:message].should match(/the error/) + + chain[1][:exception][:class].should match(/CauseException/) + chain[1][:exception][:message].should match(/the cause/) + end + + else + it 'sends only the last exception in the trace attribute' do + data = Rollbar.send(:exception_data, rescued_exception) + body = data[:body] + + body[:trace].should be_kind_of(Hash) + body[:trace_chain].should be_nil + + body[:trace][:exception][:class].should match(/StandardError/) + body[:trace][:exception][:message].should match(/the error/) + end + end + end end context 'logger' do diff --git a/spec/support/cause_exception.rb b/spec/support/cause_exception.rb new file mode 100644 index 00000000..0a79f583 --- /dev/null +++ b/spec/support/cause_exception.rb @@ -0,0 +1 @@ +class CauseException < StandardError; end