Struct error_stack::Report
source · [−]#[repr(transparent)]pub struct Report<C> { /* private fields */ }
Expand description
Contains a Frame
stack consisting of Context
s and attachments.
Attachments can be added by using attach()
. The Frame
stack can be iterated by using
frames()
.
When creating a Report
by using new()
, the passed Context
is used to set the current
context on the Report
. To provide a new one, use change_context()
.
Attachments, and objects provide
d by a Context
, are directly retrievable by calling
request_ref()
or request_value()
.
Backtrace
and SpanTrace
Report
is able to provide
a Backtrace
and a SpanTrace
, which can be retrieved by
calling backtrace()
or span_trace()
respectively. If the root context provide
s a
Backtrace
or a SpanTrace
, those are returned, otherwise, if configured, an attempt is
made to capture them when creating a Report
. To enable capturing of the backtrace, make sure
RUST_BACKTRACE
or RUST_LIB_BACKTRACE
is set according to the
Backtrace
documentation. To enable capturing of the span trace, an
ErrorLayer
has to be enabled. Please also see the Feature Flags section.
Examples
Provide a context for an error:
use error_stack::{IntoReport, ResultExt, Result};
let config_path = "./path/to/config.file";
let content = std::fs::read_to_string(config_path)
.report()
.attach_printable_lazy(|| format!("Failed to read config file {config_path:?}"))?;
...
Enforce a context for an error:
use std::{fmt, path::{Path, PathBuf}};
use error_stack::{Context, IntoReport, Report, ResultExt};
#[derive(Debug)]
enum RuntimeError {
InvalidConfig(PathBuf),
...
}
#[derive(Debug)]
enum ConfigError {
IoError,
...
}
impl fmt::Display for RuntimeError {
...
}
impl fmt::Display for ConfigError {
...
}
impl Context for RuntimeError {}
impl Context for ConfigError {}
fn read_config(path: impl AsRef<Path>) -> Result<String, Report<ConfigError>> {
std::fs::read_to_string(path.as_ref()).report().change_context(ConfigError::IoError)
}
fn main() -> Result<(), Report<RuntimeError>> {
let config_path = "./path/to/config.file";
let config = read_config(config_path)
.change_context_lazy(|| RuntimeError::InvalidConfig(PathBuf::from(config_path)))?;
...
}
Implementations
sourceimpl<C> Report<C>
impl<C> Report<C>
sourcepub fn new(context: C) -> Self where
C: Context,
pub fn new(context: C) -> Self where
C: Context,
Creates a new Report<Context>
from a provided scope.
If context
does not provide Backtrace
/SpanTrace
then this attempts to capture
them directly. Please see the Backtrace
and SpanTrace
section of the Report
documentation for more information.
Examples found in repository?
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
fn start_experiments(
experiment_ids: &[usize],
experiment_descriptions: &[&str],
) -> Result<Vec<u64>, ExperimentError> {
let experiments = experiment_ids
.iter()
.map(|exp_id| {
let description = experiment_descriptions.get(*exp_id).ok_or_else(|| {
Report::new(ExperimentError)
.attach_printable(format!("Experiment {exp_id} has no valid description"))
})?;
let experiment = parse_experiment(description)
.attach_printable(format!("Experiment {exp_id} could not be parsed"))
.change_context(ExperimentError)?;
Ok(move || experiment.0 * experiment.1)
})
.collect::<Result<Vec<_>, ExperimentError>>()
.attach_printable("Unable to setup experiments")?;
Ok(experiments.iter().map(|experiment| experiment()).collect())
}
sourcepub fn attach<A>(self, attachment: A) -> Self where
A: Send + Sync + 'static,
pub fn attach<A>(self, attachment: A) -> Self where
A: Send + Sync + 'static,
Adds additional information to the Frame
stack.
This behaves like attach_printable()
but will not be shown when printing the Report
.
To benefit from seeing attachments in normal error outputs, use attach_printable()
Note: attach_printable()
will be deprecated when specialization is stabilized and
it becomes possible to merge these two methods.
sourcepub fn attach_printable<A>(self, attachment: A) -> Self where
A: Display + Debug + Send + Sync + 'static,
pub fn attach_printable<A>(self, attachment: A) -> Self where
A: Display + Debug + Send + Sync + 'static,
Adds additional (printable) information to the Frame
stack.
This behaves like attach()
but the display implementation will be called when
printing the Report
.
Note: This will be deprecated in favor of attach()
when specialization is
stabilized it becomes possible to merge these two methods.
Example
use std::{fmt, fs};
use error_stack::{IntoReport, ResultExt};
#[derive(Debug)]
pub struct Suggestion(&'static str);
impl fmt::Display for Suggestion {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.write_str(self.0)
}
}
let error = fs::read_to_string("config.txt")
.report()
.attach(Suggestion("Better use a file which exists next time!"));
let report = error.unwrap_err();
let suggestion = report.request_ref::<Suggestion>().next().unwrap();
assert_eq!(suggestion.0, "Better use a file which exists next time!");
Examples found in repository?
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
fn start_experiments(
experiment_ids: &[usize],
experiment_descriptions: &[&str],
) -> Result<Vec<u64>, ExperimentError> {
let experiments = experiment_ids
.iter()
.map(|exp_id| {
let description = experiment_descriptions.get(*exp_id).ok_or_else(|| {
Report::new(ExperimentError)
.attach_printable(format!("Experiment {exp_id} has no valid description"))
})?;
let experiment = parse_experiment(description)
.attach_printable(format!("Experiment {exp_id} could not be parsed"))
.change_context(ExperimentError)?;
Ok(move || experiment.0 * experiment.1)
})
.collect::<Result<Vec<_>, ExperimentError>>()
.attach_printable("Unable to setup experiments")?;
Ok(experiments.iter().map(|experiment| experiment()).collect())
}
sourcepub fn change_context<T>(self, context: T) -> Report<T> where
T: Context,
pub fn change_context<T>(self, context: T) -> Report<T> where
T: Context,
sourcepub fn backtrace(&self) -> Option<&Backtrace>
Available on nightly
and crate feature std
only.
pub fn backtrace(&self) -> Option<&Backtrace>
nightly
and crate feature std
only.Returns the backtrace of the error, if captured.
Note, that RUST_BACKTRACE
or RUST_LIB_BACKTRACE
has to be set to enable backtraces.
sourcepub fn span_trace(&self) -> Option<&SpanTrace>
Available on crate feature spantrace
only.
pub fn span_trace(&self) -> Option<&SpanTrace>
spantrace
only.Returns the span trace of the error, if captured.
Note, that ErrorLayer
has to be enabled to enable span traces.
sourcepub const fn frames(&self) -> Frames<'_>ⓘNotable traits for Frames<'r>impl<'r> Iterator for Frames<'r> type Item = &'r Frame;
pub const fn frames(&self) -> Frames<'_>ⓘNotable traits for Frames<'r>impl<'r> Iterator for Frames<'r> type Item = &'r Frame;
Returns an iterator over the Frame
stack of the report.
Examples found in repository?
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
fn main() -> Result<(), MapError> {
// This hook will be executed instead of the default implementation when `Debug` is called
Report::set_debug_hook(|report, fmt| {
#[derive(Serialize)]
struct Context {
context: String,
attachments: Vec<String>,
}
let mut output = Vec::new();
let mut attachments = Vec::new();
for frame in report.frames() {
match frame.kind() {
FrameKind::Context(context) => {
output.push(Context {
context: context.to_string(),
attachments: attachments.clone(),
});
attachments.clear();
}
FrameKind::Attachment(AttachmentKind::Printable(attachment)) => {
attachments.push(attachment.to_string());
}
FrameKind::Attachment(_) => {}
}
}
if fmt.alternate() {
fmt.write_str(&serde_json::to_string_pretty(&output).expect("Could not format report"))
} else {
fmt.write_str(&serde_json::to_string(&output).expect("Could not format report"))
}
})
.expect("Hook was set twice");
let mut config = HashMap::default();
// Create an entry with "foo" as key
create_new_entry(&mut config, "foo", 1).attach_printable("Could not create new entry")?;
// Purposefully cause an error by attempting to create another entry with "foo" as key
create_new_entry(&mut config, "foo", 2).attach_printable("Could not create new entry")?;
// Will output something like
// ```json
// [
// {
// "context": "Entry \"foo\" is already occupied by 1",
// "attachments": [
// "Could not create new entry"
// ]
// }
// ]
// ```
Ok(())
}
sourcepub fn frames_mut(&mut self) -> FramesMut<'_>ⓘNotable traits for FramesMut<'r>impl<'r> Iterator for FramesMut<'r> type Item = &'r mut Frame;
pub fn frames_mut(&mut self) -> FramesMut<'_>ⓘNotable traits for FramesMut<'r>impl<'r> Iterator for FramesMut<'r> type Item = &'r mut Frame;
Returns an iterator over the Frame
stack of the report with mutable elements.
sourcepub const fn request_ref<T: ?Sized + Send + Sync + 'static>(
&self
) -> RequestRef<'_, T>ⓘNotable traits for RequestRef<'r, T>impl<'r, T> Iterator for RequestRef<'r, T> where
T: ?Sized + 'static, type Item = &'r T;
Available on nightly
only.
pub const fn request_ref<T: ?Sized + Send + Sync + 'static>(
&self
) -> RequestRef<'_, T>ⓘNotable traits for RequestRef<'r, T>impl<'r, T> Iterator for RequestRef<'r, T> where
T: ?Sized + 'static, type Item = &'r T;
T: ?Sized + 'static, type Item = &'r T;
nightly
only.sourcepub const fn request_value<T: Send + Sync + 'static>(
&self
) -> RequestValue<'_, T>ⓘNotable traits for RequestValue<'r, T>impl<'r, T> Iterator for RequestValue<'r, T> where
T: 'static, type Item = T;
Available on nightly
only.
pub const fn request_value<T: Send + Sync + 'static>(
&self
) -> RequestValue<'_, T>ⓘNotable traits for RequestValue<'r, T>impl<'r, T> Iterator for RequestValue<'r, T> where
T: 'static, type Item = T;
T: 'static, type Item = T;
nightly
only.sourcepub fn downcast_ref<T: Send + Sync + 'static>(&self) -> Option<&T>
pub fn downcast_ref<T: Send + Sync + 'static>(&self) -> Option<&T>
Searches the frame stack for a context provider T
and returns the most recent context
found.
T
can either be an attachment or a Context
.
Example
use std::io;
fn read_file(path: impl AsRef<Path>) -> Result<String, Report<io::Error>> {
...
}
let report = read_file("test.txt").unwrap_err();
let io_error = report.downcast_ref::<io::Error>().unwrap();
assert_eq!(io_error.kind(), io::ErrorKind::NotFound);
sourceimpl<T: Context> Report<T>
impl<T: Context> Report<T>
sourcepub fn current_context(&self) -> &T where
T: Send + Sync + 'static,
pub fn current_context(&self) -> &T where
T: Send + Sync + 'static,
Returns the current context of the Report
.
If the user want to get the latest context, current_context
can be called. If the user
wants to handle the error, the context can then be used to directly access the context’s
type. This is only possible for the latest context as the Report does not have multiple
generics as this would either require variadic generics or a workaround like tuple-list.
This is one disadvantage of the library in comparison to plain Errors, as in these cases, all context types are known.
Example
use std::io;
fn read_file(path: impl AsRef<Path>) -> Result<String, Report<io::Error>> {
...
}
let report = read_file("test.txt").unwrap_err();
let io_error = report.current_context();
assert_eq!(io_error.kind(), io::ErrorKind::NotFound);
sourceimpl Report<()>
impl Report<()>
sourcepub fn set_debug_hook<H>(hook: H) -> Result<(), HookAlreadySet> where
H: Fn(&Self, &mut Formatter<'_>) -> Result + Send + Sync + 'static,
Available on crate feature hooks
only.
pub fn set_debug_hook<H>(hook: H) -> Result<(), HookAlreadySet> where
H: Fn(&Self, &mut Formatter<'_>) -> Result + Send + Sync + 'static,
hooks
only.Globally sets a hook which is called when formatting Report
with the Debug
trait.
By intercepting the default Debug
implementation, this hook adds the possibility for
downstream crates to provide their own formatting like colored output or a machine-readable
output (i.e. JSON).
If not set, Debug
will print
Errors
- Returns an error if a debug hook was already set
Example
use std::io::{Error, ErrorKind};
use error_stack::{report, Report};
Report::set_debug_hook(|_, fmt| write!(fmt, "custom debug implementation"))?;
let report = report!(Error::from(ErrorKind::InvalidInput));
assert_eq!(format!("{report:?}"), "custom debug implementation");
Examples found in repository?
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
fn main() -> Result<(), MapError> {
// This hook will be executed instead of the default implementation when `Debug` is called
Report::set_debug_hook(|report, fmt| {
#[derive(Serialize)]
struct Context {
context: String,
attachments: Vec<String>,
}
let mut output = Vec::new();
let mut attachments = Vec::new();
for frame in report.frames() {
match frame.kind() {
FrameKind::Context(context) => {
output.push(Context {
context: context.to_string(),
attachments: attachments.clone(),
});
attachments.clear();
}
FrameKind::Attachment(AttachmentKind::Printable(attachment)) => {
attachments.push(attachment.to_string());
}
FrameKind::Attachment(_) => {}
}
}
if fmt.alternate() {
fmt.write_str(&serde_json::to_string_pretty(&output).expect("Could not format report"))
} else {
fmt.write_str(&serde_json::to_string(&output).expect("Could not format report"))
}
})
.expect("Hook was set twice");
let mut config = HashMap::default();
// Create an entry with "foo" as key
create_new_entry(&mut config, "foo", 1).attach_printable("Could not create new entry")?;
// Purposefully cause an error by attempting to create another entry with "foo" as key
create_new_entry(&mut config, "foo", 2).attach_printable("Could not create new entry")?;
// Will output something like
// ```json
// [
// {
// "context": "Entry \"foo\" is already occupied by 1",
// "attachments": [
// "Could not create new entry"
// ]
// }
// ]
// ```
Ok(())
}
sourcepub fn set_display_hook<H>(hook: H) -> Result<(), HookAlreadySet> where
H: Fn(&Self, &mut Formatter<'_>) -> Result + Send + Sync + 'static,
Available on crate feature hooks
only.
pub fn set_display_hook<H>(hook: H) -> Result<(), HookAlreadySet> where
H: Fn(&Self, &mut Formatter<'_>) -> Result + Send + Sync + 'static,
hooks
only.Globally sets a hook that is called when formatting Report
with the Display
trait.
By intercepting the default Display
implementation, this hook adds the possibility
for downstream crates to provide their own formatting like colored output or a
machine-readable output (i.e. JSON).
If not set, Display
will print the latest error and, if alternate formatting is enabled
("{:#}"
) and it exists, its direct cause.
Errors
- Returns an error if a display hook was already set
Example
use std::io::{Error, ErrorKind};
use error_stack::{report, Report};
Report::set_display_hook(|_, fmt| write!(fmt, "custom display implementation"))?;
let report = report!(Error::from(ErrorKind::InvalidInput));
assert_eq!(report.to_string(), "custom display implementation");
Trait Implementations
Auto Trait Implementations
impl<C> !RefUnwindSafe for Report<C>
impl<C> Send for Report<C> where
C: Send,
impl<C> Sync for Report<C> where
C: Sync,
impl<C> Unpin for Report<C> where
C: Unpin,
impl<C> !UnwindSafe for Report<C>
Blanket Implementations
sourceimpl<T> BorrowMut<T> for T where
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
const: unstable · sourcefn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
sourceimpl<T> Instrument for T
impl<T> Instrument for T
sourcefn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
sourcefn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
sourceimpl<T> WithSubscriber for T
impl<T> WithSubscriber for T
sourcefn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> where
S: Into<Dispatch>,
fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> where
S: Into<Dispatch>,
Attaches the provided Subscriber
to this type, returning a
WithDispatch
wrapper. Read more
sourcefn with_current_subscriber(self) -> WithDispatch<Self>
fn with_current_subscriber(self) -> WithDispatch<Self>
Attaches the current default Subscriber
to this type, returning a
WithDispatch
wrapper. Read more