Skip to main content

observability_core/
error.rs

1//! Error types for the observability plugin system
2
3use thiserror::Error;
4
5/// Result type for observability operations
6pub type ObservabilityResult<T> = Result<T, ObservabilityError>;
7
8/// Comprehensive error types for observability operations
9#[derive(Error, Debug, Clone)]
10pub enum ObservabilityError {
11    /// Configuration errors
12    #[error("Configuration error: {message}")]
13    Configuration { message: String },
14
15    /// Serialization/deserialization errors
16    #[error("Serialization error: {message}")]
17    Serialization { message: String },
18
19    /// Network/transport errors
20    #[error("Transport error: {message}")]
21    Transport { message: String },
22
23    /// Trace context propagation errors
24    #[error("Trace context error: {message}")]
25    TraceContext { message: String },
26
27    /// Metric collection errors
28    #[error("Metric error: {message}")]
29    Metric { message: String },
30
31    /// Logging errors
32    #[error("Logging error: {message}")]
33    Logging { message: String },
34
35    /// Batching system errors
36    #[error("Batching error: {message}")]
37    Batching { message: String },
38
39    /// Buffer overflow or memory errors
40    #[error("Buffer error: {message}")]
41    Buffer { message: String },
42
43    /// Feature not enabled
44    #[error("Feature not enabled: {feature}")]
45    FeatureNotEnabled { feature: String },
46
47    /// Generic errors for compatibility
48    #[error("Generic error: {message}")]
49    Generic { message: String },
50}
51
52impl ObservabilityError {
53    /// Create a configuration error
54    pub fn configuration(message: impl Into<String>) -> Self {
55        Self::Configuration {
56            message: message.into(),
57        }
58    }
59
60    /// Create a serialization error
61    pub fn serialization(message: impl Into<String>) -> Self {
62        Self::Serialization {
63            message: message.into(),
64        }
65    }
66
67    /// Create a transport error
68    pub fn transport(message: impl Into<String>) -> Self {
69        Self::Transport {
70            message: message.into(),
71        }
72    }
73
74    /// Create a trace context error
75    pub fn trace_context(message: impl Into<String>) -> Self {
76        Self::TraceContext {
77            message: message.into(),
78        }
79    }
80
81    /// Create a metric error
82    pub fn metric(message: impl Into<String>) -> Self {
83        Self::Metric {
84            message: message.into(),
85        }
86    }
87
88    /// Create a logging error
89    pub fn logging(message: impl Into<String>) -> Self {
90        Self::Logging {
91            message: message.into(),
92        }
93    }
94
95    /// Create a batching error
96    pub fn batching(message: impl Into<String>) -> Self {
97        Self::Batching {
98            message: message.into(),
99        }
100    }
101
102    /// Create a buffer error
103    pub fn buffer(message: impl Into<String>) -> Self {
104        Self::Buffer {
105            message: message.into(),
106        }
107    }
108
109    /// Create a feature not enabled error
110    pub fn feature_not_enabled(feature: impl Into<String>) -> Self {
111        Self::FeatureNotEnabled {
112            feature: feature.into(),
113        }
114    }
115
116    /// Create a generic error
117    pub fn generic(message: impl Into<String>) -> Self {
118        Self::Generic {
119            message: message.into(),
120        }
121    }
122}
123
124#[cfg(feature = "structured-logging")]
125impl From<serde_json::Error> for ObservabilityError {
126    fn from(err: serde_json::Error) -> Self {
127        Self::serialization(err.to_string())
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn test_each_variant_constructor_and_display() {
137        let cases: Vec<(ObservabilityError, &str)> = vec![
138            (
139                ObservabilityError::configuration("bad config"),
140                "Configuration error: bad config",
141            ),
142            (
143                ObservabilityError::serialization("parse fail"),
144                "Serialization error: parse fail",
145            ),
146            (
147                ObservabilityError::transport("conn refused"),
148                "Transport error: conn refused",
149            ),
150            (
151                ObservabilityError::trace_context("missing header"),
152                "Trace context error: missing header",
153            ),
154            (
155                ObservabilityError::metric("overflow"),
156                "Metric error: overflow",
157            ),
158            (
159                ObservabilityError::logging("init failed"),
160                "Logging error: init failed",
161            ),
162            (
163                ObservabilityError::batching("queue full"),
164                "Batching error: queue full",
165            ),
166            (ObservabilityError::buffer("oom"), "Buffer error: oom"),
167            (
168                ObservabilityError::feature_not_enabled("otel"),
169                "Feature not enabled: otel",
170            ),
171            (
172                ObservabilityError::generic("unknown"),
173                "Generic error: unknown",
174            ),
175        ];
176
177        for (err, expected_display) in cases {
178            assert_eq!(err.to_string(), expected_display);
179        }
180    }
181
182    #[test]
183    fn test_error_is_clone_and_debug() {
184        let err = ObservabilityError::configuration("test");
185        let cloned = err.clone();
186        assert_eq!(err.to_string(), cloned.to_string());
187        let debug = format!("{:?}", err);
188        assert!(debug.contains("Configuration"));
189    }
190
191    #[test]
192    fn test_result_type_alias() {
193        let ok: ObservabilityResult<u32> = Ok(42);
194        assert_eq!(ok.unwrap(), 42);
195
196        let err: ObservabilityResult<u32> = Err(ObservabilityError::generic("fail"));
197        assert!(err.is_err());
198    }
199
200    #[cfg(feature = "structured-logging")]
201    #[test]
202    fn test_from_serde_json_error() {
203        let bad_json = "not json";
204        let serde_err = serde_json::from_str::<serde_json::Value>(bad_json).unwrap_err();
205        let obs_err: ObservabilityError = serde_err.into();
206        assert!(
207            obs_err.to_string().starts_with("Serialization error:"),
208            "got: {}",
209            obs_err
210        );
211    }
212
213    #[test]
214    fn test_constructor_accepts_string_and_str() {
215        let _from_str = ObservabilityError::configuration("literal");
216        let _from_string = ObservabilityError::configuration(String::from("owned"));
217        let _from_format = ObservabilityError::configuration(format!("formatted {}", 42));
218    }
219}