pub mod metrics;
pub mod tracing;
pub mod health;
pub mod telemetry;
pub mod alerts;
use std::sync::Arc;
use tokio::sync::RwLock;
use crate::server::config::Config;
use crate::server::brp_client::BrpClient;
use crate::server::error::Result;
pub use metrics::{MetricsCollector, MCP_METRICS};
pub use tracing::TracingService;
pub use health::{HealthService, HealthStatus};
pub use telemetry::TelemetryService;
pub struct ObservabilityService {
config: Config,
metrics: Arc<MetricsCollector>,
tracing_service: Arc<TracingService>,
health_service: Arc<HealthService>,
telemetry_service: Arc<TelemetryService>,
}
impl ObservabilityService {
pub async fn new(config: Config, brp_client: Arc<RwLock<BrpClient>>) -> Result<Self> {
println!("Initializing observability stack");
let metrics = Arc::new(MetricsCollector::new(&config)?);
let tracing_service = Arc::new(TracingService::new(&config).await?);
let health_service = Arc::new(HealthService::new(brp_client.clone()).await?);
let telemetry_service = Arc::new(TelemetryService::new(&config).await?);
println!("Observability stack initialized successfully");
Ok(Self {
config,
metrics,
tracing_service,
health_service,
telemetry_service,
})
}
pub async fn start(&self) -> Result<()> {
println!("Starting observability services");
self.metrics.start().await?;
self.tracing_service.start().await?;
self.health_service.start().await?;
self.telemetry_service.start().await?;
println!("All observability services started");
Ok(())
}
pub async fn shutdown(&self) -> Result<()> {
println!("Shutting down observability services");
if let Err(e) = self.telemetry_service.shutdown().await {
println!("Error shutting down telemetry service: {}", e);
}
if let Err(e) = self.health_service.shutdown().await {
println!("Error shutting down health service: {}", e);
}
if let Err(e) = self.tracing_service.shutdown().await {
println!("Error shutting down tracing service: {}", e);
}
if let Err(e) = self.metrics.shutdown().await {
println!("Error shutting down metrics service: {}", e);
}
println!("Observability services shutdown complete");
Ok(())
}
pub fn metrics(&self) -> Arc<MetricsCollector> {
self.metrics.clone()
}
pub fn tracing(&self) -> Arc<TracingService> {
self.tracing_service.clone()
}
pub fn health(&self) -> Arc<HealthService> {
self.health_service.clone()
}
pub fn telemetry(&self) -> Arc<TelemetryService> {
self.telemetry_service.clone()
}
}
pub struct ObservabilityMetrics {
pub request_latency_p50: f64,
pub request_latency_p95: f64,
pub request_latency_p99: f64,
pub error_rate_by_tool: std::collections::HashMap<String, f64>,
pub active_connections: u64,
pub memory_usage_bytes: u64,
pub cpu_usage_percent: f64,
pub brp_connection_healthy: bool,
}
impl Default for ObservabilityMetrics {
fn default() -> Self {
Self {
request_latency_p50: 0.0,
request_latency_p95: 0.0,
request_latency_p99: 0.0,
error_rate_by_tool: std::collections::HashMap::new(),
active_connections: 0,
memory_usage_bytes: 0,
cpu_usage_percent: 0.0,
brp_connection_healthy: false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_observability_service_creation() {
let config = Config::default();
let brp_client = Arc::new(RwLock::new(crate::server::brp_client::BrpClient::new(&config)));
let observability = ObservabilityService::new(config, brp_client).await;
assert!(observability.is_ok());
}
}