1pub trait LogError<T>: Sized {
3 fn log(self) -> Option<T> {
8 self.log_as(tracing::log::Level::Error)
9 }
10 fn log_context(self, ctx: &str) -> Option<T> {
12 self.log_context_as(tracing::log::Level::Error, ctx)
13 }
14 fn log_with_context<Ctx: Fn() -> String>(self, ctx: Ctx) -> Option<T> {
16 self.log_with_context_as(tracing::log::Level::Error, ctx)
17 }
18
19 fn log_as(self, level: tracing::log::Level) -> Option<T>;
24 fn log_context_as(self, level: tracing::log::Level, ctx: &str) -> Option<T> {
26 self.log_with_context_as(level, || ctx.into())
27 }
28 fn log_with_context_as<Ctx: Fn() -> String>(
30 self,
31 level: tracing::log::Level,
32 ctx: Ctx,
33 ) -> Option<T>;
34
35 fn log_passthrough(self) -> Self {
40 self.log_as_passthrough(tracing::log::Level::Error)
41 }
42 fn log_context_passthrough(self, ctx: &str) -> Self {
44 self.log_context_as_passthrough(tracing::log::Level::Error, ctx)
45 }
46 fn log_with_context_passthrough<Ctx: Fn() -> String>(self, ctx: Ctx) -> Self {
48 self.log_with_context_as_passthrough(tracing::log::Level::Error, ctx)
49 }
50
51 fn log_as_passthrough(self, level: tracing::log::Level) -> Self;
56 fn log_context_as_passthrough(self, level: tracing::log::Level, ctx: &str) -> Self {
58 self.log_with_context_as_passthrough(level, || ctx.into())
59 }
60 fn log_with_context_as_passthrough<Ctx: Fn() -> String>(
62 self,
63 level: tracing::log::Level,
64 ctx: Ctx,
65 ) -> Self;
66}
67
68impl<T, E: std::fmt::Display + 'static> LogError<T> for Result<T, E> {
69 fn log_as(self, level: tracing::log::Level) -> Option<T> {
70 self.log_as_passthrough(level).ok()
71 }
72
73 fn log_with_context_as<Ctx: Fn() -> String>(
74 self,
75 level: tracing::log::Level,
76 ctx: Ctx,
77 ) -> Option<T> {
78 self.log_with_context_as_passthrough(level, ctx).ok()
79 }
80
81 fn log_as_passthrough(self, level: tracing::log::Level) -> Self {
82 self.map_err(|e| {
83 let es = display_error(&e);
84 log!(level, "{es}");
85 e
86 })
87 }
88
89 fn log_with_context_as_passthrough<Ctx: Fn() -> String>(
90 self,
91 level: tracing::log::Level,
92 ctx: Ctx,
93 ) -> Self {
94 self.map_err(|e| {
95 let ctx = ctx();
96 let es = display_error(&e);
97 log!(level, "error: `{ctx}` - {es}");
98 e
99 })
100 }
101}
102
103macro_rules! log {
104 ($level:expr, $($args:tt),*) => {
105 match $level {
106 tracing::log::Level::Error => tracing::error!($($args),*),
107 tracing::log::Level::Warn => tracing::warn!($($args),*),
108 tracing::log::Level::Info => tracing::info!($($args),*),
109 tracing::log::Level::Debug => tracing::debug!($($args),*),
110 tracing::log::Level::Trace => tracing::trace!($($args),*),
111 };
112 };
113}
114pub(crate) use log;
115
116pub fn display_error<E: std::fmt::Display + 'static>(e: &E) -> String {
120 match (e as &dyn std::any::Any).downcast_ref::<anyhow::Error>() {
121 Some(nehau) => {
122 let mut s = String::new();
123 format_anyhow(nehau, &mut s).unwrap();
124 s
125 }
126 None => format!("{e}"),
127 }
128}
129
130fn format_anyhow<W: std::fmt::Write>(e: &anyhow::Error, f: &mut W) -> std::fmt::Result {
131 write!(f, "{}", e)?;
132 for i in e.chain().skip(1) {
133 write!(f, ", caused by: {}", i)?;
134 }
135 write!(f, "\nstack backtrace:\n{}", e.backtrace())
136}