Skip to content

Commit

Permalink
Merge pull request #206 from rollbar/add-backtrace-to-exceptions
Browse files Browse the repository at this point in the history
Add backtrace to exceptions without backtrace. Closes #205.
  • Loading branch information
brianr committed Feb 5, 2015
2 parents d1289b2 + b83a61b commit 2fa998f
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 15 deletions.
6 changes: 6 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ gem 'racc', :platform => :rbx
gem 'minitest', :platform => :rbx
gem 'rubysl-test-unit', :platform => :rbx
gem 'rubinius-developer_tools', :platform => :rbx

if RUBY_VERSION.chars.first.to_i > 1
gem 'byebug'
else
gem 'debugger'
end
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,23 @@ Rollbar.silenced {
}
```

# Sending backtrace without rescued exceptions

If you use the gem in this way:

```ruby
exception = MyException.new('this is a message')
Rollbar.error(exception)
```

You will notice a backtrace doesn't appear in your Rollbar dashboard. This is because `exception.backtrace` is `nil` in these cases. We can send the current backtrace for you even your exception doesn't have it. In order to enable this feature you should configure Rollbar in this way:

```ruby
Rollbar.configure do |config|
config.populate_empty_backtraces = true
end
```

## Delayed::Job integration

If `delayed_job` is defined, Rollbar will automatically install a plugin that reports any uncaught exceptions that occur in jobs.
Expand Down
49 changes: 34 additions & 15 deletions lib/rollbar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -339,23 +339,20 @@ def build_payload_body_exception(message, exception, extra)
end

def trace_data(exception)
# parse backtrace
if exception.backtrace.respond_to?( :map )
frames = exception.backtrace.map { |frame|
# parse the line
match = frame.match(/(.*):(\d+)(?::in `([^']+)')?/)
if match
{ :filename => match[1], :lineno => match[2].to_i, :method => match[3] }
else
{ :filename => "<unknown>", :lineno => 0, :method => frame }
end
}
# reverse so that the order is as rollbar expects
frames.reverse!
else
frames = []
frames = exception_backtrace(exception).map do |frame|
# parse the line
match = frame.match(/(.*):(\d+)(?::in `([^']+)')?/)

if match
{ :filename => match[1], :lineno => match[2].to_i, :method => match[3] }
else
{ :filename => "<unknown>", :lineno => 0, :method => frame }
end
end

# reverse so that the order is as rollbar expects
frames.reverse!

{
:frames => frames,
:exception => {
Expand All @@ -365,6 +362,28 @@ def trace_data(exception)
}
end

# Returns the backtrace to be sent to our API. There are 3 options:
#
# 1. The exception received has a backtrace, then that backtrace is returned.
# 2. configuration.populate_empty_backtraces is disabled, we return [] here
# 3. The user has configuration.populate_empty_backtraces is enabled, then:
#
# We want to send the caller as backtrace, but the first lines of that array
# are those from the user's Rollbar.error line until this method. We want
# to remove those lines.
def exception_backtrace(exception)
return exception.backtrace if exception.backtrace.respond_to?( :map )
return [] unless configuration.populate_empty_backtraces

caller_backtrace = caller
caller_backtrace.shift while caller_backtrace[0].include?(rollbar_lib_gem_dir)
caller_backtrace
end

def rollbar_lib_gem_dir
Gem::Specification.find_by_name('rollbar').gem_dir + '/lib'
end

def trace_chain(exception)
traces = [trace_data(exception)]
visited = [exception]
Expand Down
2 changes: 2 additions & 0 deletions lib/rollbar/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class Configuration
attr_accessor :person_id_method
attr_accessor :person_username_method
attr_accessor :person_email_method
attr_accessor :populate_empty_backtraces
attr_accessor :report_dj_data
attr_accessor :request_timeout
attr_accessor :root
Expand Down Expand Up @@ -67,6 +68,7 @@ def initialize
@person_username_method = 'username'
@person_email_method = 'email'
@project_gems = []
@populate_empty_backtraces = false
@report_dj_data = true
@request_timeout = 3
@scrub_fields = [:passwd, :password, :password_confirmation, :secret,
Expand Down
19 changes: 19 additions & 0 deletions spec/rollbar_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,25 @@
payload["data"][:body][:trace][:exception][:message].should == "oops"
end

it 'gets the backtrace from the caller' do
Rollbar.configure do |config|
config.populate_empty_backtraces = true
end

exception = Exception.new

Rollbar.error(exception)

gem_dir = Gem::Specification.find_by_name('rollbar').gem_dir
gem_lib_dir = gem_dir + '/lib'
last_report = Rollbar.last_report

filepaths = last_report[:body][:trace][:frames].map {|frame| frame[:filename] }.reverse

expect(filepaths[0]).not_to include(gem_lib_dir)
expect(filepaths.any? {|filepath| filepath.include?(gem_dir) }).to be_true
end

it 'should return the exception data with a uuid, on platforms with SecureRandom' do
if defined?(SecureRandom) and SecureRandom.respond_to?(:uuid)
exception_data = Rollbar.error(StandardError.new("oops"))
Expand Down

0 comments on commit 2fa998f

Please sign in to comment.