1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 Dante Doménech Martinez dante19031999@gmail.com
use crate::service::serror::FeatureDisabledError;
#[cfg(feature = "network")]
use crate::service::HttpError;
use std::fmt::{Display, Formatter};
use std::sync::{MutexGuard, PoisonError};
/// Represents the possible failure modes for a [`Service`][`crate::Service`].
///
/// This enum wraps specific backend errors into a unified type, allowing
/// the [`LoggerImpl`][`crate::LoggerImpl`] to handle various failure scenarios consistently.
#[derive(Debug)]
pub enum ServiceError {
/// Errors related to the file system or standard I/O operations.
///
/// This is typically triggered by permission issues, missing files, or
/// interrupted streams during log rotation or local storage.
Io(std::io::Error),
/// Errors occurring during string formatting operations.
///
/// Usually happens when writing to a buffer or formatting complex
/// log structures for output.
Fmt(std::fmt::Error),
/// Errors occurring during JSON serialization or deserialization.
///
/// Triggered when the log message cannot be converted to valid JSON
/// or when a configuration file has an invalid JSON format.
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
Json(serde_json::Error),
/// Errors occurring during network requests (e.g., HTTP).
///
/// This includes connection timeouts, DNS resolution failures, or
/// transport-level issues when sending logs to a remote server.
#[cfg(feature = "network")]
#[cfg_attr(docsrs, doc(cfg(feature = "network")))]
Network(reqwest::Error),
/// Errors returned by the HTTP server or protocol.
///
/// Unlike [`ServiceError::Network`], this variant represents valid HTTP
/// responses that indicate a failure (e.g., 4xx or 5xx status codes).
#[cfg(feature = "network")]
#[cfg_attr(docsrs, doc(cfg(feature = "network")))]
Http(HttpError),
/// A synchronization primitive has been "poisoned".
///
/// This occurs when a thread panics while holding a [`std::sync::Mutex`]
/// or [`std::sync::RwLock`]. The data protected by the lock might be
/// in an inconsistent state.
LockPoisoned,
/// Failed to map the raw configuration into the internal settings structure.
///
/// This indicates that the configuration exists and is syntactically correct,
/// but contains invalid values or missing required fields.
ConfigDeserialization,
/// The requested operation requires a feature that is not currently enabled.
///
/// Check the [`FeatureDisabledError`] for details on which feature is missing
/// and how to enable it in your `Cargo.toml`.
FeatureDisabled(FeatureDisabledError),
/// A catch-all for errors not covered by the specific variants.
///
/// Use this for wrapping third-party errors that don't justify
/// a dedicated variant or for rare, unexpected conditions.
Unknown(Box<dyn std::error::Error + Send + Sync>),
}
impl Display for ServiceError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Io(err) => write!(f, "IO error: {}", err),
Self::Fmt(err) => write!(f, "Fmt error: {}", err),
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
Self::Json(err) => write!(f, "JSON error: {}", err),
#[cfg(feature = "network")]
#[cfg_attr(docsrs, doc(cfg(feature = "network")))]
Self::Network(err) => write!(f, "Network error: {}", err),
#[cfg(feature = "network")]
#[cfg_attr(docsrs, doc(cfg(feature = "network")))]
Self::Http(err) => write!(f, "Http error: {}", err.status_code()),
Self::LockPoisoned => write!(f, "Lock poisoned"),
Self::ConfigDeserialization => write!(f, "Config deserialization error"),
Self::FeatureDisabled(err) => write!(f, "{}", err),
Self::Unknown(err) => write!(f, "Unknown service error: {}", err),
}
}
}
impl std::error::Error for ServiceError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(err) => Some(err),
Self::Fmt(err) => Some(err),
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
Self::Json(err) => Some(err),
#[cfg(feature = "network")]
#[cfg_attr(docsrs, doc(cfg(feature = "network")))]
Self::Network(err) => Some(err),
#[cfg(feature = "network")]
#[cfg_attr(docsrs, doc(cfg(feature = "network")))]
Self::Http(err) => Some(err),
Self::LockPoisoned => None,
Self::ConfigDeserialization => None,
Self::FeatureDisabled(err) => Some(err),
Self::Unknown(err) => Some(err.as_ref()),
}
}
}
impl From<std::fmt::Error> for ServiceError {
fn from(err: std::fmt::Error) -> Self {
ServiceError::Fmt(err)
}
}
impl From<std::io::Error> for ServiceError {
fn from(err: std::io::Error) -> Self {
ServiceError::Io(err)
}
}
impl<T> From<PoisonError<MutexGuard<'_, T>>> for ServiceError {
fn from(_err: PoisonError<MutexGuard<'_, T>>) -> Self {
ServiceError::LockPoisoned
}
}