Skip to main content

apollo_opentelemetry/
error.rs

1//! Error types for telemetry initialization.
2
3use std::fmt;
4
5use apollo_errors::Error;
6use miette::Diagnostic;
7
8pub use crate::config::processor::RateLimitProcessorConfigError;
9
10/// Exporter type for consistent error reporting and metrics.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
12pub enum ExporterKind {
13    OtlpHttp,
14    OtlpGrpc,
15    DatadogNative,
16    DatadogHttp,
17    DatadogGrpc,
18    NewRelicHttp,
19    NewRelicGrpc,
20    HoneycombHttp,
21    HoneycombGrpc,
22    GrafanaCloudHttp,
23    Console,
24}
25
26impl ExporterKind {
27    pub fn as_str(&self) -> &'static str {
28        match self {
29            ExporterKind::OtlpHttp => "otlp_http",
30            ExporterKind::OtlpGrpc => "otlp_grpc",
31            ExporterKind::DatadogNative => "datadog_native",
32            ExporterKind::DatadogHttp => "datadog_http",
33            ExporterKind::DatadogGrpc => "datadog_grpc",
34            ExporterKind::NewRelicHttp => "new_relic_http",
35            ExporterKind::NewRelicGrpc => "new_relic_grpc",
36            ExporterKind::HoneycombHttp => "honeycomb_http",
37            ExporterKind::HoneycombGrpc => "honeycomb_grpc",
38            ExporterKind::GrafanaCloudHttp => "grafana_cloud_http",
39            ExporterKind::Console => "console",
40        }
41    }
42}
43
44impl fmt::Display for ExporterKind {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        f.write_str(self.as_str())
47    }
48}
49
50/// Errors that can occur during telemetry initialization.
51#[derive(Debug, Error, Diagnostic)]
52pub enum InitError {
53    /// Failed to create an exporter.
54    #[error("failed to create {exporter} exporter: {reason}")]
55    #[diagnostic(code(telemetry::exporter))]
56    Exporter {
57        #[extension]
58        exporter: ExporterKind,
59        #[extension]
60        reason: String,
61    },
62
63    /// Failed to create a processor.
64    #[error("failed to create {processor} processor: {reason}")]
65    #[diagnostic(code(telemetry::processor))]
66    Processor {
67        #[extension]
68        processor: String,
69        #[extension]
70        reason: String,
71    },
72
73    /// Failed to set up a bridge (e.g., log or tracing bridge).
74    #[error("failed to set up {bridge} bridge: {reason}")]
75    #[diagnostic(code(telemetry::bridge))]
76    Bridge {
77        #[extension]
78        bridge: String,
79        #[extension]
80        reason: String,
81    },
82
83    /// Rate-limited processor configuration is invalid.
84    #[error(transparent)]
85    #[diagnostic(transparent)]
86    RateLimitProcessorConfig(#[from] RateLimitProcessorConfigError),
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn exporter_error_display() {
95        let err = InitError::Exporter {
96            exporter: ExporterKind::OtlpHttp,
97            reason: "connection failed".to_string(),
98        };
99        let display = format!("{}", err);
100        assert!(display.contains("otlp_http"));
101        assert!(display.contains("connection failed"));
102    }
103
104    #[test]
105    fn processor_error_display() {
106        let err = InitError::Processor {
107            processor: "batch".to_string(),
108            reason: "invalid config".to_string(),
109        };
110        let display = format!("{}", err);
111        assert!(display.contains("batch"));
112        assert!(display.contains("invalid config"));
113    }
114
115    #[test]
116    fn bridge_error_display() {
117        let err = InitError::Bridge {
118            bridge: "tracing".to_string(),
119            reason: "already initialized".to_string(),
120        };
121        let display = format!("{}", err);
122        assert!(display.contains("tracing"));
123        assert!(display.contains("already initialized"));
124    }
125
126    #[test]
127    fn error_is_debug() {
128        let err = InitError::Exporter {
129            exporter: ExporterKind::OtlpHttp,
130            reason: "test reason".to_string(),
131        };
132        let debug = format!("{:?}", err);
133        assert!(debug.contains("Exporter"));
134    }
135}