pub mod log; pub mod multi; pub mod noop; pub mod otel; pub mod traits; pub mod verbose; pub use self::log::LogObserver; pub use self::multi::MultiObserver; pub use noop::NoopObserver; pub use otel::OtelObserver; pub use traits::{Observer, ObserverEvent}; pub use verbose::VerboseObserver; use crate::config::ObservabilityConfig; /// Factory: create the right observer from config pub fn create_observer(config: &ObservabilityConfig) -> Box { match config.backend.as_str() { "log" => Box::new(LogObserver::new()), "otel" | "opentelemetry" | "otlp" => { match OtelObserver::new( config.otel_endpoint.as_deref(), config.otel_service_name.as_deref(), ) { Ok(obs) => { tracing::info!( endpoint = config .otel_endpoint .as_deref() .unwrap_or("http://localhost:4318"), "OpenTelemetry observer initialized" ); Box::new(obs) } Err(e) => { tracing::error!("Failed to create OTel observer: {e}. Falling back to noop."); Box::new(NoopObserver) } } } "none" | "noop" => Box::new(NoopObserver), _ => { tracing::warn!( "Unknown observability backend '{}', falling back to noop", config.backend ); Box::new(NoopObserver) } } } #[cfg(test)] mod tests { use super::*; #[test] fn factory_none_returns_noop() { let cfg = ObservabilityConfig { backend: "none".into(), ..ObservabilityConfig::default() }; assert_eq!(create_observer(&cfg).name(), "noop"); } #[test] fn factory_noop_returns_noop() { let cfg = ObservabilityConfig { backend: "noop".into(), ..ObservabilityConfig::default() }; assert_eq!(create_observer(&cfg).name(), "noop"); } #[test] fn factory_log_returns_log() { let cfg = ObservabilityConfig { backend: "log".into(), ..ObservabilityConfig::default() }; assert_eq!(create_observer(&cfg).name(), "log"); } #[test] fn factory_otel_returns_otel() { let cfg = ObservabilityConfig { backend: "otel".into(), otel_endpoint: Some("http://127.0.0.1:19999".into()), otel_service_name: Some("test".into()), }; assert_eq!(create_observer(&cfg).name(), "otel"); } #[test] fn factory_opentelemetry_alias() { let cfg = ObservabilityConfig { backend: "opentelemetry".into(), otel_endpoint: Some("http://127.0.0.1:19999".into()), otel_service_name: Some("test".into()), }; assert_eq!(create_observer(&cfg).name(), "otel"); } #[test] fn factory_otlp_alias() { let cfg = ObservabilityConfig { backend: "otlp".into(), otel_endpoint: Some("http://127.0.0.1:19999".into()), otel_service_name: Some("test".into()), }; assert_eq!(create_observer(&cfg).name(), "otel"); } #[test] fn factory_unknown_falls_back_to_noop() { let cfg = ObservabilityConfig { backend: "xyzzy_unknown".into(), ..ObservabilityConfig::default() }; assert_eq!(create_observer(&cfg).name(), "noop"); } #[test] fn factory_empty_string_falls_back_to_noop() { let cfg = ObservabilityConfig { backend: String::new(), ..ObservabilityConfig::default() }; assert_eq!(create_observer(&cfg).name(), "noop"); } #[test] fn factory_garbage_falls_back_to_noop() { let cfg = ObservabilityConfig { backend: "xyzzy_garbage_123".into(), ..ObservabilityConfig::default() }; assert_eq!(create_observer(&cfg).name(), "noop"); } }