llm_cost_ops/observability/
mod.rs1pub mod config;
4pub mod metrics;
5pub mod tracing;
6pub mod logging;
7pub mod health;
8
9pub 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
43pub 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
54pub 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
65pub fn init_observability(config: &ObservabilityConfig) -> Result<ObservabilityStack, String> {
67 ObservabilityStack::init(config)
68}
69
70pub struct ObservabilityStack {
72 pub metrics: Option<MetricsRegistry>,
73 pub health: Option<HealthChecker>,
74 config: ObservabilityConfig,
75}
76
77impl ObservabilityStack {
78 pub fn init(config: &ObservabilityConfig) -> Result<Self, String> {
80 config.validate()?;
82
83 if config.tracing.enabled {
85 tracing::init_tracing(&config.tracing)?;
86 }
87
88 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 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 pub fn metrics(&self) -> Option<&MetricsRegistry> {
114 self.metrics.as_ref()
115 }
116
117 pub fn health(&self) -> Option<&HealthChecker> {
119 self.health.as_ref()
120 }
121
122 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}