1use std::{
9 error::Error as StdError,
10 fmt::{self, Display},
11 io::{self, Write as _},
12 result,
13 sync::Arc,
14};
15
16pub use crate::env_level::EnvLevelError;
17#[cfg(feature = "multi-thread")]
18use crate::{sink::Task, RecordOwned};
19
20#[derive(Debug)]
22#[non_exhaustive]
23pub enum Error {
24 FormatRecord(fmt::Error),
28
29 WriteRecord(io::Error),
34
35 FlushBuffer(io::Error),
39
40 CreateDirectory(io::Error),
44
45 OpenFile(io::Error),
49
50 QueryFileMetadata(io::Error),
55
56 RenameFile(io::Error),
60
61 RemoveFile(io::Error),
65
66 ParseLevel(String),
71
72 InvalidArgument(InvalidArgumentError),
74
75 #[cfg(feature = "multi-thread")]
79 SendToChannel(SendToChannelError, SendToChannelErrorDropped),
80
81 #[cfg(feature = "runtime-pattern")]
86 BuildPattern(BuildPatternError),
87
88 #[cfg(feature = "serde")]
92 SerializeRecord(io::Error),
93
94 Downstream(Box<dyn StdError + Send + Sync>),
101
102 Multiple(Vec<Error>),
104
105 #[cfg(test)]
106 #[doc(hidden)]
107 __ForInternalTestsUseOnly(i32),
108}
109
110impl StdError for Error {}
111
112impl Display for Error {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 match self {
115 Self::FormatRecord(err) => write!(f, "format record error: {err}"),
116 Self::WriteRecord(err) => write!(f, "write record error: {err}"),
117 Self::FlushBuffer(err) => write!(f, "flush buffer error: {err}"),
118 Self::CreateDirectory(err) => write!(f, "create directory error: {err}"),
119 Self::OpenFile(err) => write!(f, "open file error: {err}"),
120 Self::QueryFileMetadata(err) => write!(f, "query file metadata error: {err}"),
121 Self::RenameFile(err) => write!(f, "rename file error: {err}"),
122 Self::RemoveFile(err) => write!(f, "remove file error: {err}"),
123 Self::ParseLevel(level_str) => {
124 write!(f, "attempted to convert a string that doesn't match an existing log level: {level_str}")
125 }
126 Self::InvalidArgument(err) => write!(f, "invalid argument {err}"),
127 #[cfg(feature = "multi-thread")]
128 Self::SendToChannel(err, _) => write!(f, "failed to send message to channel: {err}"),
129 #[cfg(feature = "runtime-pattern")]
130 Self::BuildPattern(err) => write!(f, "failed to build pattern at runtime: {err}"),
131 #[cfg(feature = "serde")]
132 Self::SerializeRecord(err) => write!(f, "failed to serialize log: {err}"),
133 Self::Downstream(err) => write!(f, "{err}"),
134 Self::Multiple(errs) => write!(f, "{errs:?}"),
135 #[cfg(test)]
136 Self::__ForInternalTestsUseOnly(i) => write!(f, "{i}"),
137 }
138 }
139}
140
141impl From<InvalidArgumentError> for Error {
142 fn from(err: InvalidArgumentError) -> Self {
143 Self::InvalidArgument(err)
144 }
145}
146
147#[derive(Debug)]
149#[non_exhaustive]
150pub enum InvalidArgumentError {
151 LoggerName(SetLoggerNameError),
158
159 RotationPolicy(String),
165
166 #[deprecated(
168 since = "0.5.0",
169 note = "non-zero thread pool capacity is now guarded by NonZeroUsize type"
170 )]
171 ThreadPoolCapacity(String),
172}
173
174impl StdError for InvalidArgumentError {}
175
176impl Display for InvalidArgumentError {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 match self {
179 Self::LoggerName(err) => write!(f, "'logger name': {err}"),
180 Self::RotationPolicy(value) => write!(f, "'rotation policy': {value}"),
181 #[allow(deprecated)]
182 Self::ThreadPoolCapacity(value) => write!(f, "'thread pool capacity': {value}"),
183 }
184 }
185}
186
187impl From<SetLoggerNameError> for InvalidArgumentError {
188 fn from(err: SetLoggerNameError) -> Self {
189 Self::LoggerName(err)
190 }
191}
192
193#[derive(Debug)]
199pub struct SetLoggerNameError {
200 name: String,
201}
202
203impl SetLoggerNameError {
204 #[must_use]
205 pub(crate) fn new(name: impl Into<String>) -> Self {
206 Self { name: name.into() }
207 }
208
209 #[cfg(test)]
210 #[must_use]
211 pub(crate) fn name(&self) -> &str {
212 &self.name
213 }
214}
215
216impl StdError for SetLoggerNameError {}
217
218impl Display for SetLoggerNameError {
219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220 write!(f, "name '{}' contains disallowed characters", self.name)
221 }
222}
223
224#[cfg(feature = "multi-thread")]
226#[derive(Debug)]
227#[non_exhaustive]
228pub enum SendToChannelError {
229 Full,
235
236 Disconnected,
238}
239
240#[cfg(feature = "multi-thread")]
241impl StdError for SendToChannelError {}
242
243#[cfg(feature = "multi-thread")]
244impl Display for SendToChannelError {
245 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246 match self {
247 Self::Full => write!(f, "the channel is full"),
248 Self::Disconnected => write!(f, "the channel is disconnected"),
249 }
250 }
251}
252
253#[cfg(feature = "multi-thread")]
257#[derive(Debug)]
258#[non_exhaustive]
259pub enum SendToChannelErrorDropped {
260 Record(Box<RecordOwned>), Flush,
264}
265
266impl Error {
267 pub(crate) fn push_err<T>(result: Result<T>, new: Self) -> Result<T> {
268 match result {
269 Ok(_) => Err(new),
270 Err(Self::Multiple(mut errors)) => {
271 errors.push(new);
272 Err(Self::Multiple(errors))
273 }
274 Err(prev) => Err(Error::Multiple(vec![prev, new])),
275 }
276 }
277
278 pub(crate) fn push_result<T, N>(result: Result<T>, new: Result<N>) -> Result<T> {
279 match new {
280 Ok(_) => result,
281 Err(err) => Self::push_err(result, err),
282 }
283 }
284}
285
286#[cfg(feature = "multi-thread")]
287impl Error {
288 #[must_use]
289 pub(crate) fn from_crossbeam_send(err: crossbeam::channel::SendError<Task>) -> Self {
290 Self::SendToChannel(
291 SendToChannelError::Disconnected,
292 SendToChannelErrorDropped::from_task(err.0),
293 )
294 }
295
296 #[must_use]
297 pub(crate) fn from_crossbeam_try_send(err: crossbeam::channel::TrySendError<Task>) -> Self {
298 use crossbeam::channel::TrySendError;
299
300 let (error, dropped_task) = match err {
301 TrySendError::Full(dropped) => (SendToChannelError::Full, dropped),
302 TrySendError::Disconnected(dropped) => (SendToChannelError::Disconnected, dropped),
303 };
304
305 Self::SendToChannel(error, SendToChannelErrorDropped::from_task(dropped_task))
306 }
307}
308
309#[cfg(feature = "multi-thread")]
310impl SendToChannelErrorDropped {
311 #[must_use]
312 pub(crate) fn from_task(task: Task) -> Self {
313 match task {
314 Task::Log { record, .. } => Self::Record(Box::new(record)),
315 Task::Flush { .. } => Self::Flush,
316 #[cfg(test)]
317 Task::__ForTestUse { .. } => unreachable!(),
318 }
319 }
320}
321
322#[cfg(feature = "runtime-pattern")]
324#[derive(Debug)]
325pub struct BuildPatternError(pub(crate) spdlog_internal::pattern_parser::Error);
326
327#[cfg(feature = "runtime-pattern")]
328impl StdError for BuildPatternError {}
329
330#[cfg(feature = "runtime-pattern")]
331impl Display for BuildPatternError {
332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333 write!(f, "{}", self.0)
334 }
335}
336
337pub type Result<T> = result::Result<T, Error>;
339
340#[derive(Clone)]
348pub struct ErrorHandler(Option<Arc<dyn Fn(Error) + Send + Sync>>);
349
350impl ErrorHandler {
351 #[must_use]
353 pub fn new<F>(custom: F) -> Self
354 where
355 F: Fn(Error) + Send + Sync + 'static,
356 {
357 Self(Some(Arc::new(custom)))
358 }
359
360 pub fn call(&self, err: Error) {
362 self.call_internal("External", err);
363 }
364
365 pub(crate) fn call_internal(&self, from: impl AsRef<str>, err: Error) {
366 if let Some(handler) = &self.0 {
367 handler(err);
368 } else {
369 Self::default_impl(from, err);
370 }
371 }
372
373 fn default_impl(from: impl AsRef<str>, error: Error) {
374 if let Error::Multiple(errs) = error {
375 errs.into_iter()
376 .for_each(|err| Self::default_impl(from.as_ref(), err));
377 return;
378 }
379
380 let date = chrono::Local::now()
381 .format("%Y-%m-%d %H:%M:%S.%3f")
382 .to_string();
383
384 let _ = writeln!(
388 io::stderr(),
389 "[*** SPDLOG-RS UNHANDLED ERROR ***] [{}] [{}] {}",
390 date,
391 from.as_ref(),
392 error
393 );
394 }
395}
396
397impl<F> From<F> for ErrorHandler
398where
399 F: Fn(Error) + Send + Sync + 'static,
400{
401 fn from(handler: F) -> Self {
402 Self::new(handler)
403 }
404}
405
406impl Default for ErrorHandler {
407 fn default() -> Self {
410 Self(None)
411 }
412}
413
414impl fmt::Debug for ErrorHandler {
415 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416 f.debug_tuple("ErrorHandler")
417 .field(&self.0.as_ref().map_or("default", |_| "custom"))
418 .finish()
419 }
420}
421
422#[cfg(test)]
423mod tests {
424 use super::*;
425 use crate::test_utils::*;
426
427 #[test]
428 fn error_traits() {
429 assert_trait!(Error: Send + Sync);
430 }
431
432 #[test]
433 fn push_err() {
434 macro_rules! make_err {
435 ( $($inputs:tt)+ ) => {
436 Error::__ForInternalTestsUseOnly($($inputs)*)
437 };
438 }
439
440 assert!(matches!(
441 Error::push_err(Ok(()), make_err!(1)),
442 Err(make_err!(1))
443 ));
444
445 assert!(matches!(
446 Error::push_err::<()>(Err(make_err!(1)), make_err!(2)),
447 Err(Error::Multiple(v)) if matches!(v[..], [make_err!(1), make_err!(2)])
448 ));
449 }
450}