llm_cost_ops/observability/
mod.rs

1// Observability stack - metrics, tracing, logging, and health checks
2
3pub mod config;
4pub mod metrics;
5pub mod tracing;
6pub mod logging;
7pub mod health;
8
9// Re-export commonly used types
10pub use config::{
11    ObservabilityConfig,
12    MetricsConfig as ObservabilityMetricsConfig,
13    TracingConfig, TracingFormat,
14    LoggingConfig, LoggingFormat, HealthConfig, OtlpConfig,
15};
16
17pub use metrics::{
18    MetricsRegistry, MetricsError, Timer, start_timer,
19};
20
21pub use tracing::{
22    CorrelationId, RequestId, TraceContext,
23    init_tracing as init_tracing_with_config, create_span_with_context,
24    info_span_with_context, debug_span_with_context, trace_span_with_context,
25    warn_span_with_context, error_span_with_context,
26    extract_trace_context_from_headers, inject_trace_context_into_headers,
27    TraceContextLayer,
28};
29
30pub use logging::{
31    LogLevel, LogEntry, StructuredLogger, PerformanceLogger,
32};
33
34pub use health::{
35    HealthStatus, ComponentHealth, SystemHealth,
36    HealthCheck, HealthChecker,
37    DatabaseHealthCheck, CacheHealthCheck, ExternalServiceHealthCheck,
38    FunctionHealthCheck,
39};
40
41use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
42
43/// Initialize basic tracing (legacy function for backward compatibility)
44pub fn init_tracing() {
45    let env_filter = EnvFilter::try_from_default_env()
46        .unwrap_or_else(|_| EnvFilter::new("info"));
47
48    tracing_subscriber::registry()
49        .with(env_filter)
50        .with(tracing_subscriber::fmt::layer())
51        .init();
52}
53
54/// Initialize JSON tracing (legacy function for backward compatibility)
55pub fn init_tracing_json() {
56    let env_filter = EnvFilter::try_from_default_env()
57        .unwrap_or_else(|_| EnvFilter::new("info"));
58
59    tracing_subscriber::registry()
60        .with(env_filter)
61        .with(tracing_subscriber::fmt::layer().json())
62        .init();
63}
64
65/// Initialize observability stack with configuration
66pub fn init_observability(config: &ObservabilityConfig) -> Result<ObservabilityStack, String> {
67    ObservabilityStack::init(config)
68}
69
70/// Complete observability stack
71pub struct ObservabilityStack {
72    pub metrics: Option<MetricsRegistry>,
73    pub health: Option<HealthChecker>,
74    config: ObservabilityConfig,
75}
76
77impl ObservabilityStack {
78    /// Initialize the observability stack
79    pub fn init(config: &ObservabilityConfig) -> Result<Self, String> {
80        // Validate configuration
81        config.validate()?;
82
83        // Initialize tracing
84        if config.tracing.enabled {
85            tracing::init_tracing(&config.tracing)?;
86        }
87
88        // Initialize metrics
89        let metrics = if config.metrics.enabled {
90            Some(
91                metrics::MetricsRegistry::new(config.metrics.clone())
92                    .map_err(|e| format!("Failed to initialize metrics: {}", e))?,
93            )
94        } else {
95            None
96        };
97
98        // Initialize health checker
99        let health = if config.health.enabled {
100            Some(HealthChecker::new(config.health.clone()))
101        } else {
102            None
103        };
104
105        Ok(Self {
106            metrics,
107            health,
108            config: config.clone(),
109        })
110    }
111
112    /// Get metrics registry
113    pub fn metrics(&self) -> Option<&MetricsRegistry> {
114        self.metrics.as_ref()
115    }
116
117    /// Get health checker
118    pub fn health(&self) -> Option<&HealthChecker> {
119        self.health.as_ref()
120    }
121
122    /// Get configuration
123    pub fn config(&self) -> &ObservabilityConfig {
124        &self.config
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn test_observability_stack_init() {
134        let config = ObservabilityConfig::default();
135        let stack = ObservabilityStack::init(&config);
136        assert!(stack.is_ok());
137
138        let obs = stack.unwrap();
139        assert!(obs.metrics.is_some());
140        assert!(obs.health.is_some());
141    }
142
143    #[test]
144    fn test_observability_stack_disabled() {
145        let mut config = ObservabilityConfig::default();
146        config.metrics.enabled = false;
147        config.health.enabled = false;
148
149        let stack = ObservabilityStack::init(&config).unwrap();
150        assert!(stack.metrics.is_none());
151        assert!(stack.health.is_none());
152    }
153}