1use std::backtrace::Backtrace;
18use std::error::Error as StdError;
19use std::fmt;
20use std::panic::Location;
21use std::sync::Arc;
22
23use rocketmq_error::fields;
24use rocketmq_error::CanonicalCondition;
25use rocketmq_error::DiagnosticView;
26use rocketmq_error::Error as CanonicalError;
27use rocketmq_error::ErrorCode;
28use rocketmq_error::ErrorContext;
29use rocketmq_error::ErrorDescriptor;
30use rocketmq_error::PublicErrorView;
31use rocketmq_error::RecoveryHint;
32use rocketmq_error::SharedError;
33use thiserror::Error;
34
35pub type ObservabilityResult<T> = std::result::Result<T, ObservabilityError>;
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40pub enum ObservabilityOperation {
41 FeatureDisabled,
43 ValidateConfiguration,
45 InitializeMetrics,
47 InitializeTraces,
49 InitializeLogs,
51 InitializeSubscriber,
53 ValidateLogFilter,
55 InstallSubscriber {
57 attempted: bool,
59 installed: bool,
61 },
62 ShutdownMetrics,
64 ShutdownTraces,
66 ShutdownLogs,
68}
69
70#[derive(Debug, Error)]
75#[error("observability failure detail is available")]
76pub struct ObservabilityFailureDetail {
77 detail: String,
78}
79
80impl ObservabilityFailureDetail {
81 #[must_use]
83 pub fn detail(&self) -> &str {
84 &self.detail
85 }
86}
87
88#[derive(Clone)]
90pub struct ObservabilityError {
91 error: SharedError,
92 operation: ObservabilityOperation,
93}
94
95impl ObservabilityError {
96 #[track_caller]
97 fn source_free(
98 descriptor: &'static ErrorDescriptor,
99 operation: ObservabilityOperation,
100 context: ErrorContext,
101 ) -> Self {
102 Self {
103 error: Arc::new(CanonicalError::new(descriptor).with_context(context)),
104 operation,
105 }
106 }
107
108 #[track_caller]
109 fn caused_by(
110 descriptor: &'static ErrorDescriptor,
111 operation: ObservabilityOperation,
112 context: ErrorContext,
113 source: impl StdError + Send + Sync + 'static,
114 ) -> Self {
115 Self {
116 error: Arc::new(CanonicalError::caused_by(descriptor, source).with_context(context)),
117 operation,
118 }
119 }
120
121 #[track_caller]
122 fn with_detail(
123 descriptor: &'static ErrorDescriptor,
124 operation: ObservabilityOperation,
125 context: ErrorContext,
126 detail: impl ToString,
127 ) -> Self {
128 Self::caused_by(
129 descriptor,
130 operation,
131 context,
132 ObservabilityFailureDetail {
133 detail: detail.to_string(),
134 },
135 )
136 }
137
138 #[must_use]
140 #[track_caller]
141 pub fn feature_disabled(feature: &'static str) -> Self {
142 Self::source_free(
143 &rocketmq_error::OBSERVABILITY_FEATURE_DISABLED,
144 ObservabilityOperation::FeatureDisabled,
145 ErrorContext::new().with_text(fields::FEATURE, feature),
146 )
147 }
148
149 #[must_use]
151 #[track_caller]
152 pub fn invalid_config(detail: impl Into<String>) -> Self {
153 Self::with_detail(
154 &rocketmq_error::OBSERVABILITY_CONFIGURATION_INVALID,
155 ObservabilityOperation::ValidateConfiguration,
156 ErrorContext::new().with_secret_presence(fields::REASON_PRESENT),
157 detail.into(),
158 )
159 }
160
161 #[must_use]
163 #[track_caller]
164 pub fn metrics_init(detail: impl ToString) -> Self {
165 Self::initialization_detail(ObservabilityOperation::InitializeMetrics, "metrics", detail)
166 }
167
168 #[must_use]
170 #[track_caller]
171 pub fn metrics_init_source(source: impl StdError + Send + Sync + 'static) -> Self {
172 Self::initialization_source(ObservabilityOperation::InitializeMetrics, "metrics", source)
173 }
174
175 #[must_use]
177 #[track_caller]
178 pub fn traces_init(detail: impl ToString) -> Self {
179 Self::initialization_detail(ObservabilityOperation::InitializeTraces, "traces", detail)
180 }
181
182 #[must_use]
184 #[track_caller]
185 pub fn traces_init_source(source: impl StdError + Send + Sync + 'static) -> Self {
186 Self::initialization_source(ObservabilityOperation::InitializeTraces, "traces", source)
187 }
188
189 #[must_use]
191 #[track_caller]
192 pub fn logs_init(detail: impl ToString) -> Self {
193 Self::initialization_detail(ObservabilityOperation::InitializeLogs, "logs", detail)
194 }
195
196 #[must_use]
198 #[track_caller]
199 pub fn logs_init_source(source: impl StdError + Send + Sync + 'static) -> Self {
200 Self::initialization_source(ObservabilityOperation::InitializeLogs, "logs", source)
201 }
202
203 #[must_use]
205 #[track_caller]
206 pub fn logging_init(detail: impl ToString) -> Self {
207 Self::initialization_detail(ObservabilityOperation::InitializeSubscriber, "subscriber", detail)
208 }
209
210 #[must_use]
212 #[track_caller]
213 pub fn logging_init_source(source: impl StdError + Send + Sync + 'static) -> Self {
214 Self::initialization_source(ObservabilityOperation::InitializeSubscriber, "subscriber", source)
215 }
216
217 #[track_caller]
218 fn initialization_detail(operation: ObservabilityOperation, signal: &'static str, detail: impl ToString) -> Self {
219 Self::with_detail(
220 &rocketmq_error::OBSERVABILITY_INITIALIZATION_FAILED,
221 operation,
222 observability_failure_context(signal),
223 detail,
224 )
225 }
226
227 #[track_caller]
228 fn initialization_source(
229 operation: ObservabilityOperation,
230 signal: &'static str,
231 source: impl StdError + Send + Sync + 'static,
232 ) -> Self {
233 Self::caused_by(
234 &rocketmq_error::OBSERVABILITY_INITIALIZATION_FAILED,
235 operation,
236 observability_failure_context(signal),
237 source,
238 )
239 }
240
241 #[must_use]
243 #[track_caller]
244 pub fn invalid_log_filter(_filter: impl Into<String>, source: impl StdError + Send + Sync + 'static) -> Self {
245 Self::caused_by(
246 &rocketmq_error::OBSERVABILITY_LOG_FILTER_INVALID,
247 ObservabilityOperation::ValidateLogFilter,
248 ErrorContext::new()
249 .with_secret_presence(fields::FILTER_PRESENT)
250 .with_secret_presence(fields::ERROR_PRESENT),
251 source,
252 )
253 }
254
255 #[must_use]
257 #[track_caller]
258 pub fn invalid_log_filter_detail(_filter: impl Into<String>, detail: impl ToString) -> Self {
259 Self::with_detail(
260 &rocketmq_error::OBSERVABILITY_LOG_FILTER_INVALID,
261 ObservabilityOperation::ValidateLogFilter,
262 ErrorContext::new()
263 .with_secret_presence(fields::FILTER_PRESENT)
264 .with_secret_presence(fields::ERROR_PRESENT),
265 detail,
266 )
267 }
268
269 #[must_use]
271 #[track_caller]
272 pub fn subscriber_install_failed(attempted: bool, installed: bool) -> Self {
273 Self::source_free(
274 &rocketmq_error::OBSERVABILITY_SUBSCRIBER_INSTALLATION_FAILED,
275 ObservabilityOperation::InstallSubscriber { attempted, installed },
276 ErrorContext::new()
277 .with_bool(fields::ATTEMPTED, attempted)
278 .with_bool(fields::INSTALLED, installed),
279 )
280 }
281
282 #[must_use]
284 #[track_caller]
285 pub fn metrics_shutdown(detail: impl ToString) -> Self {
286 Self::shutdown_detail(ObservabilityOperation::ShutdownMetrics, "metrics", detail)
287 }
288
289 #[must_use]
291 #[track_caller]
292 pub fn traces_shutdown(detail: impl ToString) -> Self {
293 Self::shutdown_detail(ObservabilityOperation::ShutdownTraces, "traces", detail)
294 }
295
296 #[must_use]
298 #[track_caller]
299 pub fn logs_shutdown(detail: impl ToString) -> Self {
300 Self::shutdown_detail(ObservabilityOperation::ShutdownLogs, "logs", detail)
301 }
302
303 #[track_caller]
304 fn shutdown_detail(operation: ObservabilityOperation, signal: &'static str, detail: impl ToString) -> Self {
305 Self::with_detail(
306 &rocketmq_error::OBSERVABILITY_SHUTDOWN_FAILED,
307 operation,
308 observability_failure_context(signal),
309 detail,
310 )
311 }
312
313 #[must_use]
315 pub fn descriptor(&self) -> &'static ErrorDescriptor {
316 self.error.descriptor()
317 }
318
319 #[must_use]
321 pub fn code(&self) -> ErrorCode {
322 self.error.code()
323 }
324
325 #[must_use]
327 pub fn condition(&self) -> CanonicalCondition {
328 self.error.condition()
329 }
330
331 #[must_use]
333 pub fn recovery_hint(&self) -> RecoveryHint {
334 self.error.recovery_hint()
335 }
336
337 #[must_use]
339 pub const fn operation(&self) -> ObservabilityOperation {
340 self.operation
341 }
342
343 #[must_use]
345 pub fn context(&self) -> &ErrorContext {
346 self.error.context()
347 }
348
349 #[must_use]
351 pub fn location(&self) -> &'static Location<'static> {
352 self.error.location()
353 }
354
355 #[must_use]
357 pub fn backtrace(&self) -> Option<&Backtrace> {
358 self.error.backtrace()
359 }
360
361 pub fn public_view(&self) -> Result<PublicErrorView<'_>, rocketmq_error::ViewContextViolation> {
368 self.error.public_view()
369 }
370
371 pub fn diagnostic_view(&self) -> Result<DiagnosticView<'_>, rocketmq_error::ViewContextViolation> {
378 self.error.diagnostic_view()
379 }
380}
381
382fn observability_failure_context(signal: &'static str) -> ErrorContext {
383 ErrorContext::new()
384 .with_text(fields::OBSERVABILITY_SIGNAL, signal)
385 .with_secret_presence(fields::REASON_PRESENT)
386}
387
388impl fmt::Display for ObservabilityError {
389 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
390 fmt::Display::fmt(self.error.as_ref(), formatter)
391 }
392}
393
394impl fmt::Debug for ObservabilityError {
395 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
396 formatter
397 .debug_struct("ObservabilityError")
398 .field("code", &self.code())
399 .field("condition", &self.condition())
400 .field("operation", &self.operation)
401 .field("has_source", &self.error.source().is_some())
402 .finish()
403 }
404}
405
406impl StdError for ObservabilityError {
407 fn source(&self) -> Option<&(dyn StdError + 'static)> {
408 self.error.source()
409 }
410}