Support nested exceptions
luismiramirez opened this issue ยท 12 comments
When an exception is manually wrapped, it's possible to know the original exception by calling #cause
TODO
- Add native support for Nested exceptions
- Add nested exceptions to a test setup
For some context, imagine a piece of code like the following.
(A nice functioning example using: https://pokeapi.co)
module PokeApi
Error = Class.new(StandardError)
class Client
def pokemon(name:)
get("api/v2/pokemon/#{name}").body
end
private
def get(...)
connection.get(...)
rescue Faraday::Error
raise Error
end
def connection
Faraday. new("https://pokeapi.co/") do |faraday|
faraday.response :json
faraday.response :raise_error
end
end
end
end
In my code I rely on error.cause
for details, while still having the nice custom exceptions (with very minimal work).
poke_api = PokeApi::Client.new
begin
poke_api.pokemon(name: "definetly-invalid")
rescue PokeApi::Error => error
puts error.cause.inspect
end
But Appsignal will pick this up simply as PokeApi::Error
with no additional info.
Hi there!
But Appsignal will pick this up simply as PokeApi::Error with no additional info.
We still need to think about the solution. To me, the two main concerns are:
- The backtrace we want to show on the incident page.
- Until which level of causes do we want to show as errors can be nested ad infinitum?
@jessevdp as a nested error user? Which backtrace is the one you consider the most valuable?
I think the backtrace of the top-level error is still very important. The errors I'm wrapping are usually 3rd party libs so their backtrace is usually not as important. (Similar to "show application backtrace" vs "show full backtrace" buttons in Appsignal currently.)
Perhaps in ideal solution would be something like:
MyCustomException: <message>
...
backtrace
...
Caused by:
AnotherException: <message>
...
backtrace
...
This is definitely a type of format I've seen before.
I suppose the nesting is never actually infinite. So showing causes as far down as they'll go seems like the way to go. But of course I haven't done my research into all the edge cases that would uncover.
(I mean you might technically be able to code this into a loop, where 2 exceptions point at each other as their cause, but that shouldn't happen naturally, and could probably be detected)
Aside: for now I've used a bit of custom_data
to uncover 1 level of error.cause
. This isn't fully ideal but it surfaces just enough information for my current use. The backtraces as described above would be far better.
# somewhere in an initializer
class AppsignalNestedErrorEnricher
def report(error, handled:, severity:, context:, source: nil)
return if error.cause.nil?
Appsignal::Transaction.current.set_sample_data(
"custom_data",
error_cause: {
class: error.cause.class.name,
message: error.cause.message,
}
)
end
end
# This relies on Rails.error.report being run around the code.
Rails.error.subscribe(AppsignalNestedErrorEnricher.new)
# In ApplicationController
around_action do |_controller, action|
# used by config/initializers/appsignal_nested_error_enricher.rb
Rails.error.record(&action)
end
This is very valuable info. Thank you very much @jessevdp
This issue seems is labelled as a new feature. Our customers may want to be informed about these changes through a blog post announcement. An issue has been created in blog repo so we remember to publish a blog post about it.
If a blog post is not required, please add the [skip blogpost]
tag next time, and close the created issue.
- This issue has not had any activity in 14 days. Please provide a status update if it is still relevant. Closed it if it is no longer relevant. Or move it to another column if it's blocked or requires another look at it. - (More info)
This is a message from the daily scheduled checks.
I'd still love this feature ๐
Hehe nice! ๐ฅณ
(I was just shushing the backlog-helper BTW ๐)
Hi @jessevdp,
AppSignal for Ruby 3.5.0 was released yesterday, including support for error causes! See our changelog for details. ๐