- Tracing with custom error, it's best if you catch error in the final main and output it
With SpanTrace
use tracing_error::{ErrorLayer, SpanTrace};
use tracing_subscriber::prelude::*;
use tracing::{error, info};
#[derive(Debug)]
struct FooError {
message: &'static str,
context: SpanTrace,
}
impl FooError {
fn new(message: &'static str) -> Self {
Self {
message,
context: SpanTrace::capture(),
}
}
}
impl std::error::Error for FooError {}
impl std::fmt::Display for FooError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad(self.message)?;
write!(f, "\n\nspan backtrace:\n{}", self.context)?;
// write!(f, "\n\ndebug span backtrace: {:?}", self.context)?;
// write!(f, "\n\nalt debug span backtrace: {:#?}", self.context)?;
Ok(())
}
}
#[tracing::instrument]
fn do_something(foo: &str) -> Result<&'static str, impl std::error::Error + Send + Sync + 'static> {
info!("Doing something");
do_another_thing(42, false)
}
#[tracing::instrument]
fn do_another_thing(
answer: usize,
will_succeed: bool,
) -> Result<&'static str, impl std::error::Error + Send + Sync + 'static> {
info!("trying to do other thing");
Err(FooError::new("something broke, lol"))
}
#[tracing::instrument]
fn main() {
let json_fmt_layer = tracing_subscriber::fmt::Layer::new().json();
tracing_subscriber::registry()
.with(json_fmt_layer)
.with(ErrorLayer::default())
.init();
if let Err(e) = do_something("hello world") {
error!("error: {}", e);
std::process::exit(1);
}
}
Using eyre
with tracing
use color_eyre::eyre;
// use eyre::WrapErr;
use tracing::{error, info};
use tracing_subscriber::prelude::*;
fn try_main() -> eyre::Result<(), eyre::Report> {
info!("I am trying to parse");
let _sm_error = "abc".parse::<i32>()?;//.wrap_err("failed to parse")?;
Ok(())
}
#[allow(unused)]
fn try_main2() -> Result<(), Box<dyn std::error::Error + 'static>> {
info!("I am trying to parse");
let _sm_error = "abc".parse::<i32>()?;
Ok(())
}
// Can't have infallible main, because we want to handle it
fn main() {
let json_fmt_layer = tracing_subscriber::fmt::Layer::new().json();
tracing_subscriber::registry()
.with(json_fmt_layer)
.init();
if let Err(e) = try_main() {
error!("{:?}", e);
std::process::exit(1);
}
}
The code outputs
{"timestamp":"2021-11-30T00:59:07.786510Z","level":"INFO","fields":{"message":"I am trying to parse"},"target":"simpleerr"}
{"timestamp":"2021-11-30T00:59:07.786717Z","level":"ERROR","fields":{"message":"invalid digit found in string\n\nLocation:\n /rustc/09c42c45858d5f3aedfa670698275303a3d19afa/library/core/src/result.rs:1915:27"},"target":"simpleerr"}
Using eyre
with SpanTrace
and TracedError
-
Two ways of accomplishing this.
- Create error structs with
context
field of typeSpanTrace
, and implementstd::fmt::Display
trait as show below forFooError
- Any error type (that implements
std::error::Error
) can be converted toTracedError<E>
usingin_current_span()
, this is enough for most use cases (no backtraces just error reporting using tracing)
- Create error structs with
use color_eyre::eyre::{self, Context};
use tracing::{error, info, instrument};
use tracing_error::{ErrorLayer, SpanTrace};
use tracing_subscriber::prelude::*;
#[derive(Debug)]
struct FooError {
message: &'static str,
// This struct captures the current `tracing` span context when it is
// constructed. Later, when we display this error, we will format this
// captured span trace.
context: SpanTrace,
}
// Explicitly implementing all methods
// We can also use in_current_span()
impl FooError {
fn new(message: &'static str) -> Self {
Self {
message,
context: SpanTrace::capture(),
}
}
}
impl std::error::Error for FooError {}
impl std::fmt::Display for FooError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad(self.message)?;
write!(f, "\n\nspan backtrace:\n{}", self.context)?;
// write!(f, "\n\ndebug span backtrace: {:?}", self.context)?;
// write!(f, "\n\nalt debug span backtrace: {:#?}", self.context)?;
Ok(())
}
}
#[instrument]
fn do_something(foo: &str) -> eyre::Result<(), eyre::Report> {
info!("Doing something");
do_another_thing(42, false)
}
#[instrument]
fn do_another_thing(answer: usize, will_succeed: bool) -> eyre::Result<&'static str, eyre::Report> {
info!("trying to do other thing");
// ***********************************************************
// ***********************************************************
// Converts any error to TracedError (awesome) and automatically converted to eyre
let _k= std::fs::read_to_string("myfile.txt").in_current_span()?;
// ***********************************************************
// ***********************************************************
// Err(FooError::new("something is broken").in_current_span())?;
// to wrap underlying error
// let _k= std::fs::read_to_string("myfile.txt").in_current_span().wrap_err_with(|| format!("can't read file here"))?;
// Err(FooError::new("something broke lol")).map_err(|e| eyre::eyre!(e)) // convert to eyre explicitly
Err(FooError::new("something broke lol")).wrap_err_with(|| format!("this is broken"))
// wrap_err_with will convert underlying error to eyre compatible
}
#[instrument]
fn main() {
let json_fmt_layer = tracing_subscriber::fmt::Layer::new().json();
tracing_subscriber::registry()
.with(json_fmt_layer)
.with(ErrorLayer::default()) // for tracing errors
.init();
if let Err(e) = do_something("hello world") {
error!("error: {}", e);
std::process::exit(1);
}
}
Top comments (0)