observability_core/
error.rs1use thiserror::Error;
4
5pub type ObservabilityResult<T> = Result<T, ObservabilityError>;
7
8#[derive(Error, Debug, Clone)]
10pub enum ObservabilityError {
11 #[error("Configuration error: {message}")]
13 Configuration { message: String },
14
15 #[error("Serialization error: {message}")]
17 Serialization { message: String },
18
19 #[error("Transport error: {message}")]
21 Transport { message: String },
22
23 #[error("Trace context error: {message}")]
25 TraceContext { message: String },
26
27 #[error("Metric error: {message}")]
29 Metric { message: String },
30
31 #[error("Logging error: {message}")]
33 Logging { message: String },
34
35 #[error("Batching error: {message}")]
37 Batching { message: String },
38
39 #[error("Buffer error: {message}")]
41 Buffer { message: String },
42
43 #[error("Feature not enabled: {feature}")]
45 FeatureNotEnabled { feature: String },
46
47 #[error("Generic error: {message}")]
49 Generic { message: String },
50}
51
52impl ObservabilityError {
53 pub fn configuration(message: impl Into<String>) -> Self {
55 Self::Configuration {
56 message: message.into(),
57 }
58 }
59
60 pub fn serialization(message: impl Into<String>) -> Self {
62 Self::Serialization {
63 message: message.into(),
64 }
65 }
66
67 pub fn transport(message: impl Into<String>) -> Self {
69 Self::Transport {
70 message: message.into(),
71 }
72 }
73
74 pub fn trace_context(message: impl Into<String>) -> Self {
76 Self::TraceContext {
77 message: message.into(),
78 }
79 }
80
81 pub fn metric(message: impl Into<String>) -> Self {
83 Self::Metric {
84 message: message.into(),
85 }
86 }
87
88 pub fn logging(message: impl Into<String>) -> Self {
90 Self::Logging {
91 message: message.into(),
92 }
93 }
94
95 pub fn batching(message: impl Into<String>) -> Self {
97 Self::Batching {
98 message: message.into(),
99 }
100 }
101
102 pub fn buffer(message: impl Into<String>) -> Self {
104 Self::Buffer {
105 message: message.into(),
106 }
107 }
108
109 pub fn feature_not_enabled(feature: impl Into<String>) -> Self {
111 Self::FeatureNotEnabled {
112 feature: feature.into(),
113 }
114 }
115
116 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}