-
Notifications
You must be signed in to change notification settings - Fork 0
feat(macro): add macros to trace error including stack, trace and backtrace #154
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
Open
damjack
wants to merge
6
commits into
master
Choose a base branch
from
feat/extend-report-error
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
bbcfb09
feat: extend report_error macro with stack and backtrace
damjack 5fd2496
chore: add error-report dependenciy
damjack bf6c94e
feat: extend report_error having in the stack the error and all its s…
damjack 5b87037
feat: added new macro trace_error and added anyhow features to have t…
damjack 4f50047
fix: typo on src/macros.rs description for trace_error
damjack 4c1f890
fix: typo on src/macros.rs description for trace_anyhow_error
damjack File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| /// Emit a tracing error event for an error with rich, structured context. | ||
| /// It captures the error using the experimental Rust [std::error::Report](https://doc.rust-lang.org/stable/std/error/struct.Report.html) | ||
| /// and adding the type name as error.kind, the backtrace as error.trace and the error stack as error.message | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ```rust | ||
| /// use prima_tracing::trace_error; | ||
| /// # fn main() { | ||
| /// | ||
| /// let error = "not a number".parse::<usize>().unwrap_err(); | ||
| /// trace_error!(error, "Parsing error!"); | ||
| /// trace_error!(error, "Parsing error: {}", uid="1234"); | ||
| /// # } | ||
| /// ``` | ||
| #[macro_export] | ||
| macro_rules! trace_error { | ||
| ($error:expr, $message:expr) => { | ||
| { | ||
| $crate::trace_error!($error, $message,) | ||
| } | ||
| }; | ||
| ($error:expr, $message:expr, $($field:tt)*) => { | ||
| { | ||
| let kind = std::any::type_name_of_val(&$error); | ||
| let error_message = format!("{:#}", $error); | ||
| let stack = error_report::Report::new(&$error); | ||
| let trace = std::backtrace::Backtrace::force_capture(); | ||
|
|
||
| $crate::tracing::error!( | ||
| { | ||
| error.message = error_message, | ||
| error.kind = kind, | ||
| error.stack = ?stack, | ||
| error.trace = %trace, | ||
| $($field)* | ||
| }, | ||
| "{} {}", | ||
| $message, | ||
| $error | ||
| ); | ||
| } | ||
| }; | ||
| ($error:expr, $message:expr) => { | ||
| { | ||
| $crate::trace_error!($error, $message, {}) | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| /// Emit a tracing error event for anyhow error with rich, structured context. | ||
| /// It captures the error using the experimental Rust [std::error::Report](https://doc.rust-lang.org/stable/std/error/struct.Report.html) | ||
| /// and adding the type name as error.kind, the backtrace as error.trace and the error stack as error.message | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ```rust | ||
| /// use anyhow::anyhow | ||
| /// use prima_tracing::trace_error; | ||
| /// # fn main() { | ||
| /// | ||
| /// let error = anyhow!("an error"); | ||
| /// trace_anyhow_error!(error, "Throw error!"); | ||
| /// trace_anyhow_error!(error, "Throw error!", uid="1234"); | ||
| /// # } | ||
| /// ``` | ||
| #[cfg(feature = "anyhow")] | ||
| #[macro_export] | ||
| macro_rules! trace_anyhow_error { | ||
| ($error:expr, $message:expr) => { | ||
| { | ||
| $crate::trace_anyhow_error!($error, $message,) | ||
| } | ||
| }; | ||
| ($error:expr, $message:expr, $($field:tt)*) => { | ||
| { | ||
| let kind = std::any::type_name_of_val(&$error.root_cause()); | ||
| let error_message = format!("{:#}", $error); | ||
| let std_err: &(dyn std::error::Error + 'static) = $error.as_ref(); | ||
| let stack = error_report::Report::new(std_err); | ||
|
|
||
| $crate::tracing::error!( | ||
| { | ||
| error.message = error_message, | ||
| error.kind = kind, | ||
| error.stack = ?stack, | ||
| error.trace = %$error.backtrace(), | ||
| $($field)* | ||
| }, | ||
| "{} {:?}", | ||
| $message, | ||
| $error | ||
| ); | ||
| } | ||
| }; | ||
| ($error:expr, $message:expr) => { | ||
| { | ||
| $crate::trace_anyhow_error!($error, $message,) | ||
| } | ||
| }; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| use tracing_capture::{CaptureLayer, CapturedEvent, SharedStorage}; | ||
| use tracing_subscriber::layer::SubscriberExt; | ||
|
|
||
| #[cfg(feature = "anyhow")] | ||
| use {anyhow::anyhow, prima_tracing::trace_anyhow_error}; | ||
|
|
||
| use prima_tracing::trace_error; | ||
|
|
||
| #[test] | ||
| fn produce_trace_error() { | ||
| let error = "not a number".parse::<usize>().unwrap_err(); | ||
|
|
||
| with_test_tracing( | ||
| || { | ||
| trace_error!(error, "Parsing error!", something = "something"); | ||
| }, | ||
| |events| { | ||
| let event = events.first().unwrap(); | ||
|
|
||
| assert_eq!(event["error.message"], "invalid digit found in string"); | ||
| assert_eq!(event["error.kind"], "core::num::error::ParseIntError"); | ||
| assert_eq!( | ||
| event["error.stack"].as_debug_str(), | ||
| Some("invalid digit found in string") | ||
| ); | ||
| assert!(event["error.trace"].as_debug_str().is_some()); | ||
| assert!(event["error.trace"] | ||
| .as_debug_str() | ||
| .unwrap() | ||
| .contains("macros::produce_trace_error")); | ||
| assert_eq!(event["something"], "something"); | ||
| assert_eq!( | ||
| event["message"].as_debug_str(), | ||
| Some("Parsing error! invalid digit found in string") | ||
| ); | ||
| }, | ||
| ) | ||
| } | ||
|
|
||
| #[cfg(feature = "anyhow")] | ||
| #[test] | ||
| fn produce_trace_anyhow_error() { | ||
| let error = anyhow!("an error"); | ||
|
|
||
| with_test_tracing( | ||
| || { | ||
| trace_anyhow_error!(error, "Throw error!"); | ||
| }, | ||
| |events| { | ||
| let event = events.first().unwrap(); | ||
|
|
||
| assert_eq!(event["error.message"], "an error"); | ||
| assert_eq!(event["error.kind"], "&dyn core::error::Error"); | ||
| assert_eq!(event["error.stack"].as_debug_str(), Some("an error")); | ||
| assert_eq!( | ||
| event["error.trace"].as_debug_str(), | ||
| Some(format!("{}", error.backtrace()).as_str()) | ||
| ); | ||
| assert_eq!( | ||
| event["message"].as_debug_str(), | ||
| Some(format!("Throw error! {error:?}").as_str()) | ||
| ); | ||
| }, | ||
| ) | ||
| } | ||
|
|
||
| fn with_test_tracing<F, T>(tracing: F, tests: T) | ||
| where | ||
| F: FnOnce(), | ||
| T: FnOnce(Vec<CapturedEvent>), | ||
| { | ||
| let subscriber = tracing_subscriber::fmt().pretty().finish(); | ||
| let storage: SharedStorage = SharedStorage::default(); | ||
| let subscriber = subscriber.with(CaptureLayer::new(&storage)); | ||
|
|
||
| tracing::subscriber::with_default(subscriber, || { | ||
| tracing::info_span!("test-span").in_scope(tracing); | ||
| }); | ||
|
|
||
| let storage = storage.lock(); | ||
| let span = storage.all_spans().next().unwrap(); | ||
|
|
||
| tests(span.events().collect()); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,2 @@ | ||
| mod e2e; | ||
| mod macros; |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't understand what these definitions import, are these internal crates?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try to explain them.
error-report-> https://docs.rs/error-report/0.0.1/error_reportReport prints an error and all its sources.
Source messages will be cleaned using CleanedErrors to remove duplication from errors that include their source’s message in their own message.
The debug implementation prints each error on a separate line, while the display implementation prints all errors on one line separated by a colon. Using alternate formatting ({:#}) is identical to the debug implementation.
tracing-capture-> https://docs.rs/tracing-capture/0.1.0/tracing_capture/This crate provides a tracing Layer to capture tracing spans and events as they occur.
The captured spans and events can then be used for testing assertions (e.g., "Did a span with a specific name / target / … occur? What were its fields? Was the span closed? How many times the span was entered?" and so on).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Word of advise @damjack , the
error-reportcrate used in starsky and incentives-backend is not https://docs.rs/error-report, but https://github.com/primait/intermediaries-crates/tree/master/error-reportThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the reference
I see
tracing_captureis just imported in tests, is it just a local dev library?For
error-reportthe fact thatkinda scares me off of depending on it, could the code be ported directly here...?
Not sure if we need everything contained in the source, I get wanting to "pretty print" errors but we already depend on shaky libs as is