Skip to content

feat: create Exception from tracing event#359

Closed
seanpianka wants to merge 9 commits into
getsentry:masterfrom
seanpianka:master
Closed

feat: create Exception from tracing event#359
seanpianka wants to merge 9 commits into
getsentry:masterfrom
seanpianka:master

Conversation

@seanpianka

@seanpianka seanpianka commented Sep 10, 2021

Copy link
Copy Markdown
Contributor

This PR relates to my message in #180

This change will build an exception differently than event. It will look
for two hardcoded keys in the "extras" of an event:

  1. "error_kind" - this is optional and defaults to "Unknown"
  2. "error" - this is optional and defaults to the event's message

I am open to ideas for improving this! This PR simply gets exception
handling off the ground, but makes arbitrary assumptions about how the
tracing event will communicate the error information.

@flub flub left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the general approach to this, thanks for starting to work on it. Some high level questions I have though:

  • I would kind of expect to be able to pass an std::error::Error type in the error field of an trancing span/event and for the sentry integration to extract the type and value and backtrace just like it would do for directly capturing such error types. Can this be done?
  • I think the crate's doc comment should clearly document that the integration treats those tracing fields specially and how to use them to create sentry errors.
  • I'm not entirely sure if I'm reading this code correctly, but does this try to create an exception for any tracing event? Should it only do this for error-level events maybe?

Comment thread sentry-tracing/src/converters.rs Outdated
Comment on lines +171 to +174
let culprit = match (event.metadata().file(), event.metadata().line()) {
(Some(f), Some(l)) => Some(format!("{}L{}", f, l)),
_ => None,
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been trying to find out what this field is for and so far the SDK folks seem to be telling me that this is an internal field that's set during processing and also that it is deprecated. So we shouldn't set it here.
(see also getsentry/relay#1082)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed!

I tried to shoehorn the filename and line number of the span into the exception somehow, but perhaps this information will be included elsewhere? I am unsure about this, since there's no stacktrace available.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it would be nice to have this somewhere. But indeed without a stacktrace that seems tricky. I'm not very familiar with the event data format, https://develop.sentry.dev/sdk/event-payloads/#other-interfaces might have info on where else could be suitable.

@seanpianka

seanpianka commented Sep 10, 2021

Copy link
Copy Markdown
Contributor Author
  • I would kind of expect to be able to pass an std::error::Error type in the error field of an trancing span/event and for the sentry integration to extract the type and value and backtrace just like it would do for directly capturing such error types. Can this be done?

Yes, retrieving the error from the event is possible. The Visit implementation doesn't include the record_error method currently, so implementing that should give the layer access to this value.

However, Error::backtrace is unstable, and the error is supplied as a dyn Error, so there's no straightforward way to get the name of the concrete type (without nightly). But, the value is easy as .to_string(), and we can emulate a backtrace with Error::source.

  • I think the crate's doc comment should clearly document that the integration treats those tracing fields specially and how to use them to create sentry errors.

Agreed. My choice of field names is arbitrary, and I'd like to align with whichever fields #[tracing::instrument(err)] uses for errors or ignore the fields entirely if there's an dyn Error available.

  • I'm not entirely sure if I'm reading this code correctly, but does this try to create an exception for any tracing event? Should it only do this for error-level events maybe?

Perhaps I'm misunderstanding the on_event method in the layer, but I don't think it's creating an exception for any tracing event, only those that are ERROR level or above.

@seanpianka seanpianka changed the title feat: create Exception from keys in event extras feat: create Exception from tracing event Sep 10, 2021
@seanpianka seanpianka force-pushed the master branch 2 times, most recently from 18ee3ed to fede6f2 Compare September 12, 2021 19:43
@seanpianka

seanpianka commented Sep 12, 2021

Copy link
Copy Markdown
Contributor Author

So, I've re-worked the record_error impl using @leops' fork as an example. Now, issues reported in Sentry have their own exceptions with relevant type, title/message, and a (semi-relevant) backtrace.

The "ty" of the exception is still not great. For an error event, the type is Event path/to/file.rs:LineNumber. It's better than "Unknown", but still not great. If the underlying line of code was to move, the line number would be different and then Sentry would no longer group events for the same underlying issue.

Also, I do not see the "extras" in the issue (i.e. the latest event) in Sentry. In my testing, when annotating a function with #[tracing::instrument(err, fields(data = 1))], the field data = 1 does not appear on the event display in Sentry.

It seems some more debugging is needed 😅

@seanpianka seanpianka marked this pull request as draft September 14, 2021 01:51

@flub flub left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the delay in getting back!

Comment thread sentry-tracing/src/converters.rs
Comment thread sentry-tracing/src/converters.rs
Comment thread sentry-tracing/src/converters.rs
Comment on lines +182 to +187
let value = visitor
.json_values
.remove(DEFAULT_ERROR_VALUE_KEY)
.map(|v| v.as_str().map(|s| s.to_owned()))
.flatten()
.or_else(|| message.clone());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this whole thing still needed now that you discovered record_error?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left this branch as a fallback in the case that the event does not have any exceptions. This would at least create a single exception using what data is available from either the error or message field (in that order).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on handling this explicitly, versus assuming that at least one exception will always be present?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are sentry events required to always include an exception? We could document to use some_field_name=anyhow::Error::msg("oops") instead of error="oops"` if you need an exception? I fear that leaving in a special field name could be surprising at times?

@Swatinem Swatinem left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, but it would be nice to have a test for this.

Comment thread sentry-tracing/Cargo.toml
Comment on lines +15 to +16
sentry-core = { version = "0.23.0", path = "../sentry-core", features = ["client"] }
sentry-backtrace = { version = "0.23.0", path = "../sentry-backtrace" }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this change necessary? I don’t see these imports being used? Please revert this in that case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. This dependency is used in a particular scenario where the event visitor parsed no events from an event handled as an exception: source.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are your thoughts on handling this scenario where the event visitor parsed no events for an exception? It seems like a particularly unlikely scenario, and handling it in the current way requiring pulling in these two extra crates.

@seanpianka

Copy link
Copy Markdown
Contributor Author

lgtm, but it would be nice to have a test for this.

Which parts did you have in mind, @Swatinem? I suppose it makes sense to test the event_from_event and exception_from_event methods for the expected results, but is there anything else you think should be covered?

@seanpianka

Copy link
Copy Markdown
Contributor Author

Rebased again and force-pushed the changes.

@Swatinem

Swatinem commented Nov 3, 2021

Copy link
Copy Markdown
Contributor

I think a simple test where you supply some kind of Error type, and have that be captured correctly by sentry would be nice.

This change will build an exception differently than event. It will look
for two hardcoded keys in the "extras" of an event:
1. "error_kind" - this is optional and defaults to "Unknown"
2. "error" - this is optional and defaults to the event's message

Signed-off-by: Sean Pianka <pianka@eml.cc>
Signed-off-by: Sean Pianka <pianka@eml.cc>
Signed-off-by: Sean Pianka <pianka@eml.cc>
Signed-off-by: Sean Pianka <pianka@eml.cc>
refactor: reduce public api surface
refactor: rename visitor type
refactor: re-use parent context/transaction handling
chore: reorder trait impl to match trait

Signed-off-by: Sean Pianka <pianka@eml.cc>
Signed-off-by: Sean Pianka <pianka@eml.cc>
Signed-off-by: Sean Pianka <pianka@eml.cc>
Signed-off-by: Sean Pianka <pianka@eml.cc>
@Swatinem

Swatinem commented Jan 3, 2022

Copy link
Copy Markdown
Contributor

@seanpianka 👋🏻 what is still needed to push this over the finish line?

I might be a bit tempted to just merge this as is, as we are currently adding manual performance monitoring support and will then probably overhaul the tracing integration completely.

@Swatinem

Copy link
Copy Markdown
Contributor

I went ahead and merged the code with the latest upstream changes in #412

@Swatinem Swatinem closed this Jan 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants