Skip to content

Change some APIs for downstream crate implementors #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main-dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions spdlog/benches/spdlog-rs/compare_with_cpp_spdlog_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use spdlog::{
formatter::{pattern, PatternFormatter},
prelude::*,
sink::*,
ThreadPool,
ErrorHandler, ThreadPool,
};
use test::black_box;

Expand Down Expand Up @@ -46,21 +46,21 @@ fn bench(
.thread_pool(thread_pool)
.overflow_policy(policy)
.sink(file_sink)
.error_handler(|err| panic!("an error occurred: {err}"))
.error_handler(ErrorHandler::new(|err| panic!("an error occurred: {err}")))
.build()
.unwrap(),
);

let logger = Logger::builder()
.sink(async_sink)
.name("async_logger")
.error_handler(|err| {
.error_handler(ErrorHandler::new(|err| {
if let Error::SendToChannel(SendToChannelError::Full, _dropped_data) = err {
// ignore
} else {
panic!("an error occurred: {err}")
}
})
}))
.build()
.unwrap();

Expand Down
4 changes: 3 additions & 1 deletion spdlog/benches/spdlog-rs/log_crate_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ use test_utils::*;
fn init() {
if spdlog::init_log_crate_proxy().is_ok() {
spdlog::set_default_logger(Arc::new(build_test_logger(|b| {
b.error_handler(|err| panic!("an error occurred: {err}"))
b.error_handler(spdlog::ErrorHandler::new(|err| {
panic!("an error occurred: {err}")
}))
})));
}
log::set_max_level(log::LevelFilter::max());
Expand Down
2 changes: 1 addition & 1 deletion spdlog/benches/spdlog-rs/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl<F: Formatter> Sink for BenchSink<'_, F> {
unimplemented!()
}

fn set_error_handler(&self, _handler: Option<spdlog::ErrorHandler>) {
fn set_error_handler(&self, _handler: spdlog::ErrorHandler) {
unimplemented!()
}
}
Expand Down
8 changes: 4 additions & 4 deletions spdlog/benches/spdlog-rs/spdlog_rs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,20 @@ impl Mode {
const PANIC_ERR: fn(Error) = |err| panic!("an error occurred: {err}");

match self {
Self::Sync => PANIC_ERR,
Self::Async => move |err| {
Self::Sync => ErrorHandler::new(PANIC_ERR),
Self::Async => ErrorHandler::new(move |err| {
if let Error::SendToChannel(SendToChannelError::Full, _dropped_data) = err {
// ignore
} else {
PANIC_ERR(err);
}
},
}),
}
}
}

fn bench_any(bencher: &mut Bencher, mode: Mode, sink: Arc<dyn Sink>) {
sink.set_error_handler(Some(|err| panic!("an error occurred: {err}")));
sink.set_error_handler(ErrorHandler::new(|err| panic!("an error occurred: {err}")));

let logger = build_test_logger(|b| {
b.error_handler(mode.error_handler())
Expand Down
5 changes: 3 additions & 2 deletions spdlog/examples/04_format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ fn impl_manually() {
let style_range_begin = dest.len();

dest.write_str(&record.level().as_str().to_ascii_uppercase())
.map_err(spdlog::Error::FormatRecord)?;
.map_err(|err| spdlog::Error::FormatRecord(err.into()))?;

let style_range_end = dest.len();

writeln!(dest, " {}", record.payload()).map_err(spdlog::Error::FormatRecord)?;
writeln!(dest, " {}", record.payload())
.map_err(|err| spdlog::Error::FormatRecord(err.into()))?;

ctx.set_style_range(Some(style_range_begin..style_range_end));
Ok(())
Expand Down
6 changes: 3 additions & 3 deletions spdlog/examples/05_sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use spin::{Mutex, RwLock};
struct CollectVecSink {
level_filter: Atomic<LevelFilter>,
formatter: RwLock<Box<dyn Formatter>>,
error_handler: Atomic<Option<ErrorHandler>>,
error_handler: Atomic<ErrorHandler>,
collected: Mutex<Vec<String>>,
}

Expand All @@ -21,7 +21,7 @@ impl CollectVecSink {
Self {
level_filter: Atomic::new(LevelFilter::All),
formatter: RwLock::new(Box::new(FullFormatter::new())),
error_handler: Atomic::new(None),
error_handler: Atomic::new(ErrorHandler::default()),
collected: Mutex::new(Vec::new()),
}
}
Expand Down Expand Up @@ -58,7 +58,7 @@ impl Sink for CollectVecSink {
*self.formatter.write() = formatter;
}

fn set_error_handler(&self, handler: Option<ErrorHandler>) {
fn set_error_handler(&self, handler: ErrorHandler) {
self.error_handler.store(handler, Ordering::Relaxed);
}
}
Expand Down
169 changes: 157 additions & 12 deletions spdlog/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
//! Provides error types.
//!
//! # Default error handler
//!
//! If a logger or sink does not have an error handler set up, a default error
//! handler will be used, which will print the error to `stderr`.

use std::{
error::Error as StdError,
fmt::{self, Display},
io, result,
};
Expand All @@ -18,6 +13,81 @@ use crate::utils::const_assert;
#[cfg(feature = "multi-thread")]
use crate::{sink::Task, RecordOwned};

/// Stores an error that can either be typed or erased.
///
/// This wrapper is mainly used for returning arbitrary errors from downstream
/// implementors.
///
/// # Examples
///
/// ```
/// use std::{error::Error, fmt, io};
///
/// use spdlog::{error::ErasableError, formatter::Formatter, prelude::*, sink::Sink, Record};
///
/// #[derive(Debug)]
/// struct MyError;
///
/// impl Error for MyError {}
///
/// impl fmt::Display for MyError {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// write!(f, "MyError")
/// }
/// }
///
/// struct MySink;
///
/// impl Sink for MySink {
/// fn log(&self, record: &Record) -> spdlog::Result<()> {
/// let err: MyError = /* Something went wrong */
/// # MyError;
/// // `err` can not be converted to `io::Error`, so we use `Erased`
/// Err(spdlog::Error::WriteRecord(ErasableError::erase(MyError)))
/// }
///
/// fn flush(&self) -> spdlog::Result<()> {
/// let err: io::Error = /* Something went wrong */
/// # io::Error::new(io::ErrorKind::NotFound, "");
/// // `err` is a `io::Error`, so we use `Typed`
/// Err(spdlog::Error::FlushBuffer(err.into()))
/// }
///
/// fn level_filter(&self) -> LevelFilter /* ... */
/// # { unimplemented!() }
/// fn set_level_filter(&self, level_filter: LevelFilter) /* ... */
/// # { unimplemented!() }
/// fn set_formatter(&self, formatter: Box<dyn Formatter>) /* ... */
/// # { unimplemented!() }
/// fn set_error_handler(&self, handler: spdlog::ErrorHandler) /* ... */
/// # { unimplemented!() }
/// }
/// ```
#[derive(Error, Debug)]
pub enum ErasableError<E: StdError> {
/// A concrete error type is held, and the user can access it.
#[error("{0}")]
Typed(E),
/// The concrete type may not match, thus it is erased to a basic
/// `dyn std::error::Error`.
#[error("{0}")]
Erased(Box<dyn StdError>),
}

impl<E: StdError> ErasableError<E> {
/// Erase a typed error to a basic `Box<dyn std::error::Error>`.
#[must_use]
pub fn erase<R: StdError + 'static>(err: R) -> ErasableError<E> {
ErasableError::Erased(Box::new(err))
}
}

impl<E: StdError> From<E> for ErasableError<E> {
fn from(err: E) -> Self {
Self::Typed(err)
}
}

/// Contains most errors of this crate.
#[derive(Error, Debug)]
#[non_exhaustive]
Expand All @@ -26,20 +96,20 @@ pub enum Error {
///
/// [`Formatter`]: crate::formatter::Formatter
#[error("format record error: {0}")]
FormatRecord(fmt::Error),
FormatRecord(ErasableError<fmt::Error>),

/// Returned by [`Sink`]s when an error occurs in writing a record to the
/// target.
///
/// [`Sink`]: crate::sink::Sink
#[error("write record error: {0}")]
WriteRecord(io::Error),
WriteRecord(ErasableError<io::Error>),

/// Returned by [`Sink`]s when an error occurs in flushing the buffer.
///
/// [`Sink`]: crate::sink::Sink
#[error("flush buffer error: {0}")]
FlushBuffer(io::Error),
FlushBuffer(ErasableError<io::Error>),

/// Returned by [`Sink`]s when an error occurs in creating a directory.
///
Expand Down Expand Up @@ -264,11 +334,86 @@ pub struct BuildPatternError(pub(crate) spdlog_internal::pattern_parser::Error);
/// The result type of this crate.
pub type Result<T> = result::Result<T, Error>;

/// The error handler function type.
pub type ErrorHandler = fn(Error);
/// Represents an error handler.
///
/// Call [`ErrorHandler::new`] to construct an error handler with a custom
/// function.
///
/// Call [`ErrorHandler::default`] to construct an empty error handler, when an
/// error is triggered, a built-in fallback handler will be used which prints
/// the error to `stderr`.
#[derive(Copy, Clone, Debug)]
pub struct ErrorHandler(Option<fn(Error)>);

const_assert!(Atomic::<ErrorHandler>::is_lock_free());
const_assert!(Atomic::<Option<ErrorHandler>>::is_lock_free());

impl ErrorHandler {
/// Constructs an error handler with a custom function.
#[must_use]
pub fn new(custom: fn(Error)) -> Self {
Self(Some(custom))
}

/// Sets the error handler.
///
/// Passes `None` to use the built-in fallback handler, which prints errors
/// to `stderr`.
pub fn set(&mut self, handler: Option<fn(Error)>) {
self.0 = handler;
}

/// Calls the error handler with an error.
pub fn call(&self, err: Error) {
self.call_internal("External", err);
}

pub(crate) fn call_internal(&self, from: impl AsRef<str>, err: Error) {
if let Some(handler) = self.0 {
handler(err);
} else {
Self::default_impl(from, err);
}
}

fn default_impl(from: impl AsRef<str>, error: Error) {
if let Error::Multiple(errs) = error {
errs.into_iter()
.for_each(|err| Self::default_impl(from.as_ref(), err));
return;
}

let date = chrono::Local::now()
.format("%Y-%m-%d %H:%M:%S.%3f")
.to_string();

eprintln!(
"[*** SPDLOG-RS UNHANDLED ERROR ***] [{}] [{}] {}",
date,
from.as_ref(),
error
);
}
}

impl Default for ErrorHandler {
/// Constructs an error handler with the built-in handler which prints
/// errors to `stderr`.
fn default() -> Self {
Self(None)
}
}

// FIXME: Doesn't work as expected at the moment
// https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/impl.20From.3Cno-capture-closure.3E.20for.20fn.3F
//
// impl<F> From<F> for ErrorHandler
// where
// F: Into<fn(Error)>,
// {
// fn from(handler: F) -> Self {
// Self::new(handler.into())
// }
// }

#[cfg(test)]
mod tests {
Expand Down
2 changes: 1 addition & 1 deletion spdlog/src/formatter/full_formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl Formatter for FullFormatter {
ctx: &mut FormatterContext,
) -> crate::Result<()> {
self.format_impl(record, dest, ctx)
.map_err(Error::FormatRecord)
.map_err(|err| Error::FormatRecord(err.into()))
}
}

Expand Down
2 changes: 1 addition & 1 deletion spdlog/src/formatter/journald_formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl Formatter for JournaldFormatter {
ctx: &mut FormatterContext,
) -> crate::Result<()> {
self.format_impl(record, dest, ctx)
.map_err(Error::FormatRecord)
.map_err(|err| Error::FormatRecord(err.into()))
}
}

Expand Down
2 changes: 1 addition & 1 deletion spdlog/src/formatter/json_formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl From<serde_json::Error> for JsonFormatterError {
impl From<JsonFormatterError> for crate::Error {
fn from(value: JsonFormatterError) -> Self {
match value {
JsonFormatterError::Fmt(e) => Error::FormatRecord(e),
JsonFormatterError::Fmt(e) => Error::FormatRecord(e.into()),
JsonFormatterError::Serialization(e) => Error::SerializeRecord(e.into()),
}
}
Expand Down
7 changes: 4 additions & 3 deletions spdlog/src/formatter/pattern_formatter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ use crate::{
///
/// impl Pattern for MyPattern {
/// fn format(&self, record: &Record, dest: &mut StringBuf, _: &mut PatternContext) -> spdlog::Result<()> {
/// write!(dest, "My own pattern").map_err(spdlog::Error::FormatRecord)
/// write!(dest, "My own pattern").map_err(|err| spdlog::Error::FormatRecord(err.into()))
/// }
/// }
///
Expand Down Expand Up @@ -225,7 +225,7 @@ use crate::{
///
/// impl Pattern for MyPattern {
/// fn format(&self, record: &Record, dest: &mut StringBuf, _: &mut PatternContext) -> spdlog::Result<()> {
/// write!(dest, "{}", self.id).map_err(spdlog::Error::FormatRecord)
/// write!(dest, "{}", self.id).map_err(|err| spdlog::Error::FormatRecord(err.into()))
/// }
/// }
///
Expand Down Expand Up @@ -472,7 +472,8 @@ impl Pattern for str {
dest: &mut StringBuf,
_ctx: &mut PatternContext,
) -> crate::Result<()> {
dest.write_str(self).map_err(Error::FormatRecord)
dest.write_str(self)
.map_err(|err| Error::FormatRecord(err.into()))
}
}

Expand Down
Loading
Loading