barbacane_telemetry/
lib.rs1pub mod config;
22pub mod export;
23pub mod logging;
24pub mod metrics;
25pub mod prometheus;
26pub mod tracing;
27
28pub use config::{LogFormat, OtlpProtocol, TelemetryConfig};
29pub use logging::events;
30pub use metrics::MetricsRegistry;
31pub use prometheus::PROMETHEUS_CONTENT_TYPE;
32pub use tracing::{attributes, spans, TracingContext};
33
34use std::sync::Arc;
35use thiserror::Error;
36
37#[derive(Debug, Error)]
39pub enum TelemetryError {
40 #[error("failed to initialize logging: {0}")]
42 LoggingInit(String),
43
44 #[error("failed to initialize tracing: {0}")]
46 TracingInit(String),
47
48 #[error("failed to initialize OTLP exporter: {0}")]
50 OtlpInit(String),
51}
52
53pub struct Telemetry {
58 config: TelemetryConfig,
59 metrics: Arc<MetricsRegistry>,
60}
61
62impl Telemetry {
63 pub fn init(config: TelemetryConfig) -> Result<Self, TelemetryError> {
70 logging::init_logging(&config)?;
72
73 let metrics = Arc::new(MetricsRegistry::new());
75
76 if config.otlp_endpoint.is_some() {
78 export::init_otlp_tracer(&config)?;
79 } else {
80 tracing::init_basic_tracer(&config.service_name, config.trace_sampling);
81 }
82
83 Ok(Self { config, metrics })
84 }
85
86 pub fn init_without_logging(config: TelemetryConfig) -> Result<Self, TelemetryError> {
90 let metrics = Arc::new(MetricsRegistry::new());
92
93 if config.otlp_endpoint.is_some() {
95 export::init_otlp_tracer(&config)?;
96 } else {
97 tracing::init_basic_tracer(&config.service_name, config.trace_sampling);
98 }
99
100 Ok(Self { config, metrics })
101 }
102
103 pub fn shutdown(&self) {
107 if self.config.otlp_endpoint.is_some() {
108 export::shutdown_otlp();
109 } else {
110 tracing::shutdown_tracer();
111 }
112 }
113
114 pub fn config(&self) -> &TelemetryConfig {
116 &self.config
117 }
118
119 pub fn metrics(&self) -> &Arc<MetricsRegistry> {
121 &self.metrics
122 }
123
124 pub fn metrics_clone(&self) -> Arc<MetricsRegistry> {
126 Arc::clone(&self.metrics)
127 }
128
129 pub fn render_prometheus(&self) -> String {
131 prometheus::render_metrics(&self.metrics)
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_default_config() {
141 let config = TelemetryConfig::default();
142 assert_eq!(config.service_name, "barbacane");
143 assert_eq!(config.log_level, "info");
144 assert_eq!(config.log_format, LogFormat::Json);
145 assert!(config.otlp_endpoint.is_none());
146 assert_eq!(config.trace_sampling, 1.0);
147 }
148
149 #[test]
150 fn test_config_builder() {
151 let config = TelemetryConfig::new()
152 .with_service_name("test-service")
153 .with_log_level("debug")
154 .with_log_format(LogFormat::Pretty)
155 .with_otlp_endpoint("http://localhost:4317")
156 .with_trace_sampling(0.5);
157
158 assert_eq!(config.service_name, "test-service");
159 assert_eq!(config.log_level, "debug");
160 assert_eq!(config.log_format, LogFormat::Pretty);
161 assert_eq!(
162 config.otlp_endpoint,
163 Some("http://localhost:4317".to_string())
164 );
165 assert_eq!(config.trace_sampling, 0.5);
166 }
167
168 #[test]
169 fn test_trace_sampling_clamped() {
170 let config = TelemetryConfig::new().with_trace_sampling(1.5);
171 assert_eq!(config.trace_sampling, 1.0);
172
173 let config = TelemetryConfig::new().with_trace_sampling(-0.5);
174 assert_eq!(config.trace_sampling, 0.0);
175 }
176
177 #[test]
178 fn test_telemetry_init_without_logging() {
179 let config = TelemetryConfig::default();
180 let result = Telemetry::init_without_logging(config);
181 assert!(result.is_ok());
182 }
183}