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    fmt::{self, Display},
10    io, result,
11};
12
13use atomic::Atomic;
14use thiserror::Error;
15
16pub use crate::env_level::EnvLevelError;
17use crate::utils::const_assert;
18#[cfg(feature = "multi-thread")]
19use crate::{sink::Task, RecordOwned};
20
21/// Contains most errors of this crate.
22#[derive(Error, Debug)]
23#[non_exhaustive]
24pub enum Error {
25    /// Returned by [`Formatter`]s when an error occurs in formatting a record.
26    ///
27    /// [`Formatter`]: crate::formatter::Formatter
28    #[error("format record error: {0}")]
29    FormatRecord(fmt::Error),
30
31    /// Returned by [`Sink`]s when an error occurs in writing a record to the
32    /// target.
33    ///
34    /// [`Sink`]: crate::sink::Sink
35    #[error("write record error: {0}")]
36    WriteRecord(io::Error),
37
38    /// Returned by [`Sink`]s when an error occurs in flushing the buffer.
39    ///
40    /// [`Sink`]: crate::sink::Sink
41    #[error("flush buffer error: {0}")]
42    FlushBuffer(io::Error),
43
44    /// Returned by [`Sink`]s when an error occurs in creating a directory.
45    ///
46    /// [`Sink`]: crate::sink::Sink
47    #[error("create directory error: {0}")]
48    CreateDirectory(io::Error),
49
50    /// Returned by [`Sink`]s when an error occurs in opening a file.
51    ///
52    /// [`Sink`]: crate::sink::Sink
53    #[error("open file error: {0}")]
54    OpenFile(io::Error),
55
56    /// Returned by [`Sink`]s when an error occurs in querying the metadata of a
57    /// file.
58    ///
59    /// [`Sink`]: crate::sink::Sink
60    #[error("query file metadata error: {0}")]
61    QueryFileMetadata(io::Error),
62
63    /// Returned by [`Sink`]s when an error occurs in renaming a file.
64    ///
65    /// [`Sink`]: crate::sink::Sink
66    #[error("rename file error: {0}")]
67    RenameFile(io::Error),
68
69    /// Returned by [`Sink`]s when an error occurs in removing a file.
70    ///
71    /// [`Sink`]: crate::sink::Sink
72    #[error("remove file error: {0}")]
73    RemoveFile(io::Error),
74
75    /// Returned by [`from_str`] when the string doesn't match any of the log
76    /// levels.
77    ///
78    /// [`from_str`]: std::str::FromStr::from_str
79    #[error("attempted to convert a string that doesn't match an existing log level: {0}")]
80    ParseLevel(String),
81
82    /// Returned if an invalid argument was passed in.
83    #[error("invalid argument {0}")]
84    InvalidArgument(#[from] InvalidArgumentError),
85
86    /// Returned by [`Sink`]s when an error occurs in sending to the channel.
87    ///
88    /// [`Sink`]: crate::sink::Sink
89    #[cfg(feature = "multi-thread")]
90    #[error("failed to send message to channel: {0}")]
91    SendToChannel(SendToChannelError, SendToChannelErrorDropped),
92
93    /// Returned by [`runtime_pattern!`] when the pattern is failed to be built
94    /// at runtime.
95    ///
96    /// [`runtime_pattern!`]: crate::formatter::runtime_pattern
97    #[cfg(feature = "runtime-pattern")]
98    #[error("failed to build pattern at runtime: {0}")]
99    BuildPattern(BuildPatternError),
100
101    /// Returned by [`Formatter`]s when an error occurs in serializing a log.
102    ///
103    /// [`Formatter`]: crate::formatter::Formatter
104    #[cfg(feature = "serde")]
105    #[error("failed to serialize log: {0}")]
106    SerializeRecord(io::Error),
107
108    /// Returned when multiple errors occurred.
109    #[error("{0:?}")]
110    Multiple(Vec<Error>),
111
112    #[cfg(test)]
113    #[doc(hidden)]
114    #[error("{0}")]
115    __ForInternalTestsUseOnly(i32),
116}
117
118/// Indicates that an invalid parameter was specified.
119#[derive(Error, Debug)]
120#[non_exhaustive]
121pub enum InvalidArgumentError {
122    /// Invalid logger name.
123    ///
124    /// See the documentation of [`LoggerBuilder::name`] for the name
125    /// requirements.
126    ///
127    /// [`LoggerBuilder::name`]: crate::LoggerBuilder::name
128    #[error("'logger name': {0}")]
129    LoggerName(#[from] SetLoggerNameError),
130
131    /// Invalid [`RotationPolicy`].
132    ///
133    /// See the documentation of [`RotationPolicy`] for the input requirements.
134    ///
135    /// [`RotationPolicy`]: crate::sink::RotationPolicy
136    #[error("'rotation policy': {0}")]
137    RotationPolicy(String),
138
139    /// Invalid thread pool capacity.
140    #[error("'thread pool capacity': {0}")]
141    ThreadPoolCapacity(String),
142}
143
144/// Indicates that an invalid logger name was set.
145///
146/// See the documentation of [`LoggerBuilder::name`] for the name requirements.
147///
148/// [`LoggerBuilder::name`]: crate::LoggerBuilder::name
149#[derive(Error, Debug)]
150pub struct SetLoggerNameError {
151    name: String,
152}
153
154impl SetLoggerNameError {
155    #[must_use]
156    pub(crate) fn new(name: impl Into<String>) -> Self {
157        Self { name: name.into() }
158    }
159
160    #[cfg(test)]
161    #[must_use]
162    pub(crate) fn name(&self) -> &str {
163        &self.name
164    }
165}
166
167impl Display for SetLoggerNameError {
168    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169        write!(f, "name '{}' contains disallowed characters", self.name)
170    }
171}
172
173/// Indicates that an error occurred while sending to channel.
174#[cfg(feature = "multi-thread")]
175#[derive(Error, Debug)]
176#[non_exhaustive]
177pub enum SendToChannelError {
178    /// The channel is full.
179    ///
180    /// The variant returned only when [`OverflowPolicy::DropIncoming`] is used.
181    ///
182    /// [`OverflowPolicy::DropIncoming`]: crate::sink::async_sink::OverflowPolicy::DropIncoming
183    #[error("the channel is full")]
184    Full,
185
186    /// The channel is disconnected.
187    #[error("the channel is disconnected")]
188    Disconnected,
189}
190
191/// Contains data that is dropped after sending to the channel failed.
192///
193/// You can handle them manually or just ignore them.
194#[cfg(feature = "multi-thread")]
195#[derive(Debug)]
196#[non_exhaustive]
197pub enum SendToChannelErrorDropped {
198    /// A `log` operation and a record are dropped.
199    Record(Box<RecordOwned>), // Boxed because `RecordOwned` is a bit large.
200    /// A `flush` operation is dropped.
201    Flush,
202}
203
204impl Error {
205    pub(crate) fn push_err<T>(result: Result<T>, new: Self) -> Result<T> {
206        match result {
207            Ok(_) => Err(new),
208            Err(Self::Multiple(mut errors)) => {
209                errors.push(new);
210                Err(Self::Multiple(errors))
211            }
212            Err(prev) => Err(Error::Multiple(vec![prev, new])),
213        }
214    }
215
216    pub(crate) fn push_result<T, N>(result: Result<T>, new: Result<N>) -> Result<T> {
217        match new {
218            Ok(_) => result,
219            Err(err) => Self::push_err(result, err),
220        }
221    }
222}
223
224#[cfg(feature = "multi-thread")]
225impl Error {
226    #[must_use]
227    pub(crate) fn from_crossbeam_send(err: crossbeam::channel::SendError<Task>) -> Self {
228        Self::SendToChannel(
229            SendToChannelError::Disconnected,
230            SendToChannelErrorDropped::from_task(err.0),
231        )
232    }
233
234    #[must_use]
235    pub(crate) fn from_crossbeam_try_send(err: crossbeam::channel::TrySendError<Task>) -> Self {
236        use crossbeam::channel::TrySendError;
237
238        let (error, dropped_task) = match err {
239            TrySendError::Full(dropped) => (SendToChannelError::Full, dropped),
240            TrySendError::Disconnected(dropped) => (SendToChannelError::Disconnected, dropped),
241        };
242
243        Self::SendToChannel(error, SendToChannelErrorDropped::from_task(dropped_task))
244    }
245}
246
247#[cfg(feature = "multi-thread")]
248impl SendToChannelErrorDropped {
249    #[must_use]
250    pub(crate) fn from_task(task: Task) -> Self {
251        match task {
252            Task::Log { record, .. } => Self::Record(Box::new(record)),
253            Task::Flush { .. } => Self::Flush,
254        }
255    }
256}
257
258/// Indicates that an error occurred while building a pattern at compile-time.
259#[cfg(feature = "runtime-pattern")]
260#[derive(Error, Debug)]
261#[error("{0}")]
262pub struct BuildPatternError(pub(crate) spdlog_internal::pattern_parser::Error);
263
264/// The result type of this crate.
265pub type Result<T> = result::Result<T, Error>;
266
267/// The error handler function type.
268pub type ErrorHandler = fn(Error);
269
270const_assert!(Atomic::<ErrorHandler>::is_lock_free());
271const_assert!(Atomic::<Option<ErrorHandler>>::is_lock_free());
272
273#[cfg(test)]
274mod tests {
275    use super::*;
276
277    #[test]
278    fn push_err() {
279        macro_rules! make_err {
280            ( $($inputs:tt)+ ) => {
281                Error::__ForInternalTestsUseOnly($($inputs)*)
282            };
283        }
284
285        assert!(matches!(
286            Error::push_err(Ok(()), make_err!(1)),
287            Err(make_err!(1))
288        ));
289
290        assert!(matches!(
291            Error::push_err::<()>(Err(make_err!(1)), make_err!(2)),
292            Err(Error::Multiple(v)) if matches!(v[..], [make_err!(1), make_err!(2)])
293        ));
294    }
295}