spdlog/
error.rs

1//! Provides error types.
2//!
3//! # Default error handler
4//!
5//! If a logger or sink does not have an error handler set up, a default error
6//! handler will be used, which will print the error to `stderr`.
7
8use std::{
9    error::Error as StdError,
10    fmt::{self, Display},
11    io::{self, Write},
12    result,
13    sync::Arc,
14};
15
16use thiserror::Error;
17
18pub use crate::env_level::EnvLevelError;
19#[cfg(feature = "multi-thread")]
20use crate::{sink::Task, RecordOwned};
21
22/// Contains most errors of this crate.
23#[derive(Error, Debug)]
24#[non_exhaustive]
25pub enum Error {
26    /// Returned by [`Formatter`]s when an error occurs in formatting a record.
27    ///
28    /// [`Formatter`]: crate::formatter::Formatter
29    #[error("format record error: {0}")]
30    FormatRecord(fmt::Error),
31
32    /// Returned by [`Sink`]s when an error occurs in writing a record to the
33    /// target.
34    ///
35    /// [`Sink`]: crate::sink::Sink
36    #[error("write record error: {0}")]
37    WriteRecord(io::Error),
38
39    /// Returned by [`Sink`]s when an error occurs in flushing the buffer.
40    ///
41    /// [`Sink`]: crate::sink::Sink
42    #[error("flush buffer error: {0}")]
43    FlushBuffer(io::Error),
44
45    /// Returned by [`Sink`]s when an error occurs in creating a directory.
46    ///
47    /// [`Sink`]: crate::sink::Sink
48    #[error("create directory error: {0}")]
49    CreateDirectory(io::Error),
50
51    /// Returned by [`Sink`]s when an error occurs in opening a file.
52    ///
53    /// [`Sink`]: crate::sink::Sink
54    #[error("open file error: {0}")]
55    OpenFile(io::Error),
56
57    /// Returned by [`Sink`]s when an error occurs in querying the metadata of a
58    /// file.
59    ///
60    /// [`Sink`]: crate::sink::Sink
61    #[error("query file metadata error: {0}")]
62    QueryFileMetadata(io::Error),
63
64    /// Returned by [`Sink`]s when an error occurs in renaming a file.
65    ///
66    /// [`Sink`]: crate::sink::Sink
67    #[error("rename file error: {0}")]
68    RenameFile(io::Error),
69
70    /// Returned by [`Sink`]s when an error occurs in removing a file.
71    ///
72    /// [`Sink`]: crate::sink::Sink
73    #[error("remove file error: {0}")]
74    RemoveFile(io::Error),
75
76    /// Returned by [`from_str`] when the string doesn't match any of the log
77    /// levels.
78    ///
79    /// [`from_str`]: std::str::FromStr::from_str
80    #[error("attempted to convert a string that doesn't match an existing log level: {0}")]
81    ParseLevel(String),
82
83    /// Returned if an invalid argument was passed in.
84    #[error("invalid argument {0}")]
85    InvalidArgument(#[from] InvalidArgumentError),
86
87    /// Returned by [`Sink`]s when an error occurs in sending to the channel.
88    ///
89    /// [`Sink`]: crate::sink::Sink
90    #[cfg(feature = "multi-thread")]
91    #[error("failed to send message to channel: {0}")]
92    SendToChannel(SendToChannelError, SendToChannelErrorDropped),
93
94    /// Returned by [`runtime_pattern!`] when the pattern is failed to be built
95    /// at runtime.
96    ///
97    /// [`runtime_pattern!`]: crate::formatter::runtime_pattern
98    #[cfg(feature = "runtime-pattern")]
99    #[error("failed to build pattern at runtime: {0}")]
100    BuildPattern(BuildPatternError),
101
102    /// Returned by [`Formatter`]s when an error occurs in serializing a log.
103    ///
104    /// [`Formatter`]: crate::formatter::Formatter
105    #[cfg(feature = "serde")]
106    #[error("failed to serialize log: {0}")]
107    SerializeRecord(io::Error),
108
109    /// Returned from a downstream implementation of `spdlog-rs`. Its actual
110    /// error type may be a downstream struct.
111    ///
112    /// When downstream crates encounter errors, other more specific error
113    /// variants should be used first, this variant should only be used as a
114    /// last option when other variant types are incompatible.
115    #[error("{0}")]
116    Downstream(Box<dyn StdError>),
117
118    /// Returned when multiple errors occurred.
119    #[error("{0:?}")]
120    Multiple(Vec<Error>),
121
122    #[cfg(test)]
123    #[doc(hidden)]
124    #[error("{0}")]
125    __ForInternalTestsUseOnly(i32),
126}
127
128/// Indicates that an invalid parameter was specified.
129#[derive(Error, Debug)]
130#[non_exhaustive]
131pub enum InvalidArgumentError {
132    /// Invalid logger name.
133    ///
134    /// See the documentation of [`LoggerBuilder::name`] for the name
135    /// requirements.
136    ///
137    /// [`LoggerBuilder::name`]: crate::LoggerBuilder::name
138    #[error("'logger name': {0}")]
139    LoggerName(#[from] SetLoggerNameError),
140
141    /// Invalid [`RotationPolicy`].
142    ///
143    /// See the documentation of [`RotationPolicy`] for the input requirements.
144    ///
145    /// [`RotationPolicy`]: crate::sink::RotationPolicy
146    #[error("'rotation policy': {0}")]
147    RotationPolicy(String),
148
149    /// Invalid thread pool capacity.
150    #[deprecated(
151        since = "0.5.0",
152        note = "non-zero thread pool capacity is now guarded by NonZeroUsize type"
153    )]
154    #[error("'thread pool capacity': {0}")]
155    ThreadPoolCapacity(String),
156}
157
158/// Indicates that an invalid logger name was set.
159///
160/// See the documentation of [`LoggerBuilder::name`] for the name requirements.
161///
162/// [`LoggerBuilder::name`]: crate::LoggerBuilder::name
163#[derive(Error, Debug)]
164pub struct SetLoggerNameError {
165    name: String,
166}
167
168impl SetLoggerNameError {
169    #[must_use]
170    pub(crate) fn new(name: impl Into<String>) -> Self {
171        Self { name: name.into() }
172    }
173
174    #[cfg(test)]
175    #[must_use]
176    pub(crate) fn name(&self) -> &str {
177        &self.name
178    }
179}
180
181impl Display for SetLoggerNameError {
182    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183        write!(f, "name '{}' contains disallowed characters", self.name)
184    }
185}
186
187/// Indicates that an error occurred while sending to channel.
188#[cfg(feature = "multi-thread")]
189#[derive(Error, Debug)]
190#[non_exhaustive]
191pub enum SendToChannelError {
192    /// The channel is full.
193    ///
194    /// The variant returned only when [`OverflowPolicy::DropIncoming`] is used.
195    ///
196    /// [`OverflowPolicy::DropIncoming`]: crate::sink::async_sink::OverflowPolicy::DropIncoming
197    #[error("the channel is full")]
198    Full,
199
200    /// The channel is disconnected.
201    #[error("the channel is disconnected")]
202    Disconnected,
203}
204
205/// Contains data that is dropped after sending to the channel failed.
206///
207/// You can handle them manually or just ignore them.
208#[cfg(feature = "multi-thread")]
209#[derive(Debug)]
210#[non_exhaustive]
211pub enum SendToChannelErrorDropped {
212    /// A `log` operation and a record are dropped.
213    Record(Box<RecordOwned>), // Boxed because `RecordOwned` is a bit large.
214    /// A `flush` operation is dropped.
215    Flush,
216}
217
218impl Error {
219    pub(crate) fn push_err<T>(result: Result<T>, new: Self) -> Result<T> {
220        match result {
221            Ok(_) => Err(new),
222            Err(Self::Multiple(mut errors)) => {
223                errors.push(new);
224                Err(Self::Multiple(errors))
225            }
226            Err(prev) => Err(Error::Multiple(vec![prev, new])),
227        }
228    }
229
230    pub(crate) fn push_result<T, N>(result: Result<T>, new: Result<N>) -> Result<T> {
231        match new {
232            Ok(_) => result,
233            Err(err) => Self::push_err(result, err),
234        }
235    }
236}
237
238#[cfg(feature = "multi-thread")]
239impl Error {
240    #[must_use]
241    pub(crate) fn from_crossbeam_send(err: crossbeam::channel::SendError<Task>) -> Self {
242        Self::SendToChannel(
243            SendToChannelError::Disconnected,
244            SendToChannelErrorDropped::from_task(err.0),
245        )
246    }
247
248    #[must_use]
249    pub(crate) fn from_crossbeam_try_send(err: crossbeam::channel::TrySendError<Task>) -> Self {
250        use crossbeam::channel::TrySendError;
251
252        let (error, dropped_task) = match err {
253            TrySendError::Full(dropped) => (SendToChannelError::Full, dropped),
254            TrySendError::Disconnected(dropped) => (SendToChannelError::Disconnected, dropped),
255        };
256
257        Self::SendToChannel(error, SendToChannelErrorDropped::from_task(dropped_task))
258    }
259}
260
261#[cfg(feature = "multi-thread")]
262impl SendToChannelErrorDropped {
263    #[must_use]
264    pub(crate) fn from_task(task: Task) -> Self {
265        match task {
266            Task::Log { record, .. } => Self::Record(Box::new(record)),
267            Task::Flush { .. } => Self::Flush,
268        }
269    }
270}
271
272/// Indicates that an error occurred while building a pattern at compile-time.
273#[cfg(feature = "runtime-pattern")]
274#[derive(Error, Debug)]
275#[error("{0}")]
276pub struct BuildPatternError(pub(crate) spdlog_internal::pattern_parser::Error);
277
278/// The result type of this crate.
279pub type Result<T> = result::Result<T, Error>;
280
281/// Represents an error handler.
282///
283/// In most cases, it can be constructed by just a `.into()`.
284///
285/// Call [`ErrorHandler::default`] to construct an empty error handler, when an
286/// error is triggered, a built-in fallback handler will be used which prints
287/// the error to `stderr`.
288#[derive(Clone)]
289pub struct ErrorHandler(Option<Arc<dyn Fn(Error) + Send + Sync>>);
290
291impl ErrorHandler {
292    /// Constructs an error handler with a custom function.
293    #[must_use]
294    pub fn new<F>(custom: F) -> Self
295    where
296        F: Fn(Error) + Send + Sync + 'static,
297    {
298        Self(Some(Arc::new(custom)))
299    }
300
301    /// Calls the error handler with an error.
302    pub fn call(&self, err: Error) {
303        self.call_internal("External", err);
304    }
305
306    pub(crate) fn call_internal(&self, from: impl AsRef<str>, err: Error) {
307        if let Some(handler) = &self.0 {
308            handler(err);
309        } else {
310            Self::default_impl(from, err);
311        }
312    }
313
314    fn default_impl(from: impl AsRef<str>, error: Error) {
315        if let Error::Multiple(errs) = error {
316            errs.into_iter()
317                .for_each(|err| Self::default_impl(from.as_ref(), err));
318            return;
319        }
320
321        let date = chrono::Local::now()
322            .format("%Y-%m-%d %H:%M:%S.%3f")
323            .to_string();
324
325        // https://github.com/SpriteOvO/spdlog-rs/discussions/87
326        //
327        // Don't use `eprintln!` here, as it may fail to write and then panic.
328        let _ = writeln!(
329            io::stderr(),
330            "[*** SPDLOG-RS UNHANDLED ERROR ***] [{}] [{}] {}",
331            date,
332            from.as_ref(),
333            error
334        );
335    }
336}
337
338impl<F> From<F> for ErrorHandler
339where
340    F: Fn(Error) + Send + Sync + 'static,
341{
342    fn from(handler: F) -> Self {
343        Self::new(handler)
344    }
345}
346
347impl Default for ErrorHandler {
348    /// Constructs an error handler with the built-in handler which prints
349    /// errors to `stderr`.
350    fn default() -> Self {
351        Self(None)
352    }
353}
354
355impl fmt::Debug for ErrorHandler {
356    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357        f.debug_tuple("ErrorHandler")
358            .field(&self.0.as_ref().map_or("default", |_| "custom"))
359            .finish()
360    }
361}
362
363#[cfg(test)]
364mod tests {
365    use super::*;
366
367    #[test]
368    fn push_err() {
369        macro_rules! make_err {
370            ( $($inputs:tt)+ ) => {
371                Error::__ForInternalTestsUseOnly($($inputs)*)
372            };
373        }
374
375        assert!(matches!(
376            Error::push_err(Ok(()), make_err!(1)),
377            Err(make_err!(1))
378        ));
379
380        assert!(matches!(
381            Error::push_err::<()>(Err(make_err!(1)), make_err!(2)),
382            Err(Error::Multiple(v)) if matches!(v[..], [make_err!(1), make_err!(2)])
383        ));
384    }
385}