use std::error::Error;
use err_context::prelude::*;
use log::{log, Level};
pub type AnyError = Box<dyn Error + Send + Sync>;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[non_exhaustive]
pub enum ErrorLogFormat {
MultiLine,
SingleLine,
}
pub fn log_error(level: Level, target: &str, e: &AnyError, format: ErrorLogFormat) {
match format {
ErrorLogFormat::MultiLine => {
for cause in e.chain() {
log!(target: target, level, "{}", cause);
}
}
ErrorLogFormat::SingleLine => {
log!(target: target, level, "{}", e.display("; "));
}
}
}
#[macro_export]
macro_rules! log_error {
($level: ident, $descr: expr => $err: expr) => {
$crate::log_error!(@SingleLine, $level, $err.context($descr).into());
};
($level: ident, $err: expr) => {
$crate::log_error!(@SingleLine, $level, $err);
};
(multi $level: ident, $descr: expr => $err: expr) => {
$crate::log_error!(@MultiLine, $level, $err.context($descr).into());
};
(multi $level: ident, $err: expr) => {
$crate::log_error!(@MultiLine, $level, $err);
};
(@$format: ident, $level: ident, $err: expr) => {
$crate::error::log_error(
$crate::macro_support::Level::$level,
module_path!(),
&$err,
$crate::error::ErrorLogFormat::$format,
);
};
}
pub fn log_errors<R, F>(target: &str, f: F) -> Result<R, AnyError>
where
F: FnOnce() -> Result<R, AnyError>,
{
let result = f();
if let Err(ref e) = result {
log_error(Level::Error, target, e, ErrorLogFormat::MultiLine);
}
result
}
#[cfg(test)]
mod tests {
use std::fmt::{Display, Formatter, Result as FmtResult};
use super::*;
#[derive(Copy, Clone, Debug)]
struct Dummy;
impl Display for Dummy {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
write!(fmt, "Dummy error")
}
}
impl Error for Dummy {}
#[test]
fn log_error_macro() {
let err = Dummy;
log_error!(Debug, err.into());
log_error!(Debug, &err.into());
log_error!(Debug, err.context("Another level").into());
log_error!(Debug, "Another level" => err);
let multi_err = err.context("Another level").into();
log_error!(multi Info, multi_err);
}
}