1use 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#[derive(Error, Debug)]
24#[non_exhaustive]
25pub enum Error {
26 #[error("format record error: {0}")]
30 FormatRecord(fmt::Error),
31
32 #[error("write record error: {0}")]
37 WriteRecord(io::Error),
38
39 #[error("flush buffer error: {0}")]
43 FlushBuffer(io::Error),
44
45 #[error("create directory error: {0}")]
49 CreateDirectory(io::Error),
50
51 #[error("open file error: {0}")]
55 OpenFile(io::Error),
56
57 #[error("query file metadata error: {0}")]
62 QueryFileMetadata(io::Error),
63
64 #[error("rename file error: {0}")]
68 RenameFile(io::Error),
69
70 #[error("remove file error: {0}")]
74 RemoveFile(io::Error),
75
76 #[error("attempted to convert a string that doesn't match an existing log level: {0}")]
81 ParseLevel(String),
82
83 #[error("invalid argument {0}")]
85 InvalidArgument(#[from] InvalidArgumentError),
86
87 #[cfg(feature = "multi-thread")]
91 #[error("failed to send message to channel: {0}")]
92 SendToChannel(SendToChannelError, SendToChannelErrorDropped),
93
94 #[cfg(feature = "runtime-pattern")]
99 #[error("failed to build pattern at runtime: {0}")]
100 BuildPattern(BuildPatternError),
101
102 #[cfg(feature = "serde")]
106 #[error("failed to serialize log: {0}")]
107 SerializeRecord(io::Error),
108
109 #[error("{0}")]
116 Downstream(Box<dyn StdError>),
117
118 #[error("{0:?}")]
120 Multiple(Vec<Error>),
121
122 #[cfg(test)]
123 #[doc(hidden)]
124 #[error("{0}")]
125 __ForInternalTestsUseOnly(i32),
126}
127
128#[derive(Error, Debug)]
130#[non_exhaustive]
131pub enum InvalidArgumentError {
132 #[error("'logger name': {0}")]
139 LoggerName(#[from] SetLoggerNameError),
140
141 #[error("'rotation policy': {0}")]
147 RotationPolicy(String),
148
149 #[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#[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#[cfg(feature = "multi-thread")]
189#[derive(Error, Debug)]
190#[non_exhaustive]
191pub enum SendToChannelError {
192 #[error("the channel is full")]
198 Full,
199
200 #[error("the channel is disconnected")]
202 Disconnected,
203}
204
205#[cfg(feature = "multi-thread")]
209#[derive(Debug)]
210#[non_exhaustive]
211pub enum SendToChannelErrorDropped {
212 Record(Box<RecordOwned>), 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#[cfg(feature = "runtime-pattern")]
274#[derive(Error, Debug)]
275#[error("{0}")]
276pub struct BuildPatternError(pub(crate) spdlog_internal::pattern_parser::Error);
277
278pub type Result<T> = result::Result<T, Error>;
280
281#[derive(Clone)]
289pub struct ErrorHandler(Option<Arc<dyn Fn(Error) + Send + Sync>>);
290
291impl ErrorHandler {
292 #[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 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 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 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}