pub trait LogError<T>: Sized {
fn log(self) -> Option<T> {
self.log_as(tracing::log::Level::Error)
}
fn log_context(self, ctx: &str) -> Option<T> {
self.log_context_as(tracing::log::Level::Error, ctx)
}
fn log_with_context<Ctx: Fn() -> String>(self, ctx: Ctx) -> Option<T> {
self.log_with_context_as(tracing::log::Level::Error, ctx)
}
fn log_as(self, level: tracing::log::Level) -> Option<T>;
fn log_context_as(self, level: tracing::log::Level, ctx: &str) -> Option<T> {
self.log_with_context_as(level, || ctx.into())
}
fn log_with_context_as<Ctx: Fn() -> String>(
self,
level: tracing::log::Level,
ctx: Ctx,
) -> Option<T>;
fn log_passthrough(self) -> Self {
self.log_as_passthrough(tracing::log::Level::Error)
}
fn log_context_passthrough(self, ctx: &str) -> Self {
self.log_context_as_passthrough(tracing::log::Level::Error, ctx)
}
fn log_with_context_passthrough<Ctx: Fn() -> String>(self, ctx: Ctx) -> Self {
self.log_with_context_as_passthrough(tracing::log::Level::Error, ctx)
}
fn log_as_passthrough(self, level: tracing::log::Level) -> Self;
fn log_context_as_passthrough(self, level: tracing::log::Level, ctx: &str) -> Self {
self.log_with_context_as_passthrough(level, || ctx.into())
}
fn log_with_context_as_passthrough<Ctx: Fn() -> String>(
self,
level: tracing::log::Level,
ctx: Ctx,
) -> Self;
}
impl<T, E: std::fmt::Display + 'static> LogError<T> for Result<T, E> {
fn log_as(self, level: tracing::log::Level) -> Option<T> {
self.log_as_passthrough(level).ok()
}
fn log_with_context_as<Ctx: Fn() -> String>(
self,
level: tracing::log::Level,
ctx: Ctx,
) -> Option<T> {
self.log_with_context_as_passthrough(level, ctx).ok()
}
fn log_as_passthrough(self, level: tracing::log::Level) -> Self {
self.map_err(|e| {
let es = display_error(&e);
log!(level, "{es}");
e
})
}
fn log_with_context_as_passthrough<Ctx: Fn() -> String>(
self,
level: tracing::log::Level,
ctx: Ctx,
) -> Self {
self.map_err(|e| {
let ctx = ctx();
let es = display_error(&e);
log!(level, "error: `{ctx}` - {es}");
e
})
}
}
macro_rules! log {
($level:expr, $($args:tt),*) => {
match $level {
tracing::log::Level::Error => tracing::error!($($args),*),
tracing::log::Level::Warn => tracing::warn!($($args),*),
tracing::log::Level::Info => tracing::info!($($args),*),
tracing::log::Level::Debug => tracing::debug!($($args),*),
tracing::log::Level::Trace => tracing::trace!($($args),*),
};
};
}
pub(crate) use log;
pub fn display_error<E: std::fmt::Display + 'static>(e: &E) -> String {
match (e as &dyn std::any::Any).downcast_ref::<anyhow::Error>() {
Some(nehau) => {
let mut s = String::new();
format_anyhow(nehau, &mut s).unwrap();
s
}
None => format!("{e}"),
}
}
fn format_anyhow<W: std::fmt::Write>(e: &anyhow::Error, f: &mut W) -> std::fmt::Result {
write!(f, "{}", e)?;
for i in e.chain().skip(1) {
write!(f, ", caused by: {}", i)?;
}
write!(f, "\nstack backtrace:\n{}", e.backtrace())
}