Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Exception#detailed_message #761

Merged
merged 3 commits into from
Dec 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Changelog
=========

## TBD

### Enhancements

* Use `Exception#detailed_message` instead of `Exception#message` when available
| [#761](https://github.com/bugsnag/bugsnag-ruby/pull/761)

## v6.26.0 (1 December 2022)

### Enhancements
Expand Down
26 changes: 24 additions & 2 deletions lib/bugsnag/report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -428,9 +428,11 @@ def update_handled_counts(is_unhandled, was_unhandled)

def generate_exception_list
raw_exceptions.map do |exception|
class_name = error_class(exception)

{
errorClass: error_class(exception),
message: exception.message,
errorClass: class_name,
message: error_message(exception, class_name),
stacktrace: Stacktrace.process(exception.backtrace, configuration)
}
end
Expand All @@ -448,6 +450,26 @@ def error_class(exception)
(exception.is_a? Class) ? exception.name : exception.class.name
end

def error_message(exception, class_name)
# Ruby 3.2 added Exception#detailed_message for Gems like "Did you mean"
# to annotate an exception's message
return exception.message unless exception.respond_to?(:detailed_message)

# the "highlight" argument may add terminal escape codes to the output,
# which we don't want to include
# it _should_ always be present but it's possible to forget to add it or
# to have implemented this method before Ruby 3.2
message =
begin
exception.detailed_message(highlight: false)
rescue ArgumentError
exception.detailed_message
end

# remove the class name to be consistent with Exception#message
message.sub(" (#{class_name})", '')
end

def generate_raw_exceptions(exception)
exceptions = []

Expand Down
55 changes: 54 additions & 1 deletion spec/report_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ def gloops
end
end

if RUBY_VERSION >= '2.0.0'
require_relative './support/exception_with_detailed_message'
else
require_relative './support/exception_with_detailed_message_ruby_1'
end

class ExceptionWithDetailedMessageButNoHighlight < Exception
def detailed_message
"detail about '#{self}'"
end
end

shared_examples "Report or Event tests" do |class_to_test|
context "metadata" do
include_examples(
Expand Down Expand Up @@ -99,6 +111,17 @@ def gloops
})
end

it "uses Exception#detailed_message if available" do
exception = ExceptionWithDetailedMessage.new("some message")
report_or_event = class_to_test.new(exception, Bugsnag.configuration)

expect(report_or_event.summary).to eq({
error_class: "ExceptionWithDetailedMessage",
message: "some message with some extra detail",
severity: "warning"
})
end

it "handles empty exceptions" do
begin
1/0
Expand Down Expand Up @@ -288,6 +311,17 @@ def gloops
)
})
end

it "uses Exception#detailed_message if available" do
exception = ExceptionWithDetailedMessage.new("some message")
report_or_event = class_to_test.new(exception, Bugsnag.configuration)

expect(report_or_event.errors.length).to eq(1)

message = report_or_event.errors.first.error_message

expect(message).to eq("some message with some extra detail")
end
end

it "has a reference to the original error" do
Expand Down Expand Up @@ -1399,7 +1433,6 @@ def gloops
end

it "does not unwrap more than 5 exceptions" do

first_ex = ex = NestedException.new("Deep exception")
10.times do |idx|
ex = ex.original_exception = NestedException.new("Deep exception #{idx}")
Expand Down Expand Up @@ -1432,6 +1465,26 @@ def gloops
}
end

it "uses Exception#detailed_message if available" do
Bugsnag.notify(ExceptionWithDetailedMessage.new("some message"))

expect(Bugsnag).to have_sent_notification{ |payload, headers|
exception = get_exception_from_payload(payload)
expect(exception["errorClass"]).to eq("ExceptionWithDetailedMessage")
expect(exception["message"]).to eq("some message with some extra detail")
}
end

it "handles implementations of Exception#detailed_message with no 'highlight' parameter" do
Bugsnag.notify(ExceptionWithDetailedMessageButNoHighlight.new("some message"))

expect(Bugsnag).to have_sent_notification{ |payload, headers|
exception = get_exception_from_payload(payload)
expect(exception["errorClass"]).to eq("ExceptionWithDetailedMessageButNoHighlight")
expect(exception["message"]).to eq("detail about 'some message'")
}
end

it "supports unix-style paths in backtraces" do
ex = BugsnagTestException.new("It crashed")
ex.set_backtrace([
Expand Down
10 changes: 10 additions & 0 deletions spec/support/exception_with_detailed_message.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class ExceptionWithDetailedMessage < Exception
def detailed_message(highlight: true)
# change the output to ensure we pass the right value for "highlight"
if highlight
"\e[1m!!! #{self} !!!\e[0m"
else
"#{self} with some extra detail"
end
end
end
11 changes: 11 additions & 0 deletions spec/support/exception_with_detailed_message_ruby_1.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# ExceptionWithDetailedMessage for Ruby 1.9 (no keyword argument)
class ExceptionWithDetailedMessage < Exception
def detailed_message(params)
# change the output to ensure we pass the right value for "highlight"
if params[:highlight]
"\e[1m!!! #{self} !!!\e[0m"
else
"#{self} with some extra detail"
end
end
end