pub mod alerting;
pub mod dashboard;
pub mod jaeger_tracer;
pub mod metrics;
pub mod prometheus_exporter;
pub mod structured_logging;
pub mod visualization;
use metrics::{BusinessMetrics, CustomMetrics, PerformanceMetrics, RuntimeMetrics};
use std::collections::{BTreeMap, HashMap};
use std::net::SocketAddr;
use std::sync::RwLock;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tracing::info;
pub use dashboard::{
ChartType, Dashboard, DashboardConfig, DashboardManager, FieldConfig, GrafanaClient, GraphType,
GridPos, LegendConfig, MetricsCollector, PanelConfig, PanelOptions, QueryTarget,
TemplateVariable, ThresholdsConfig, TimeRangeConfig, TooltipConfig,
};
pub use visualization::{
AxisConfig, BarChart, BarChartBuilder, ColorPalette, DataPoint, DataSeries, EdgeStyle,
FilterConfig, ForceLayoutParams, GraphEdge, GraphNode, GridConfig, InteractionConfig,
LayoutAlgorithm, LayoutConfig, LegendConfig as VizLegendConfig, LineChart, LineChartBuilder,
LineStyle, MarkerConfig, PieChart, PieChartBuilder, Position, Size,
TooltipConfig as VizTooltipConfig, TopologyGraph, TopologyGraphBuilder, VisualizationConfig,
};
#[derive(Debug, Clone)]
pub struct ObservabilityConfig {
pub enable_prometheus: bool,
pub prometheus_addr: SocketAddr,
pub enable_structured_logging: bool,
pub log_level: tracing::Level,
pub enable_alerting: bool,
pub metrics_update_interval: std::time::Duration,
}
impl Default for ObservabilityConfig {
fn default() -> Self {
Self {
enable_prometheus: true,
prometheus_addr: "127.0.0.1:9090".parse().unwrap(),
enable_structured_logging: true,
log_level: tracing::Level::INFO,
enable_alerting: true,
metrics_update_interval: std::time::Duration::from_secs(5),
}
}
}
pub struct ObservableSystem {
config: ObservabilityConfig,
prometheus_exporter: Option<Arc<RwLock<PrometheusExporter>>>,
structured_logger: Option<StructuredLogger>,
custom_metrics: Arc<RwLock<CustomMetrics>>,
alerting_system: Option<AlertingSystem>,
}
impl ObservableSystem {
pub async fn new(config: ObservabilityConfig) -> Result<Self> {
info!("Initializing observability system...");
let mut system = Self {
config: config.clone(),
prometheus_exporter: None,
structured_logger: None,
custom_metrics: Arc::new(Mutex::new(CustomMetrics::new())),
alerting_system: None,
};
if config.enable_structured_logging {
system.structured_logger =
Some(StructuredLogger::new(config.log_level, "amberjs".to_string()));
info!("Structured logging initialized");
}
if config.enable_prometheus {
let exporter: _ = PrometheusExporter::new()?;
system.prometheus_exporter = Some(Arc::new(Mutex::new(exporter)));
info!("Prometheus exporter initialized");
}
if config.enable_alerting {
system.alerting_system = Some(AlertingSystem::new());
info!("Alerting system initialized");
}
info!("Observability system initialized successfully");
Ok(system)
}
pub fn prometheus_exporter(&self) -> Option<Arc<RwLock<PrometheusExporter>>> {
self.prometheus_exporter.clone()
}
pub fn logger(&self) -> &StructuredLogger {
self.structured_logger
.as_ref()
.expect("Structured logger not initialized")
}
pub fn custom_metrics(&self) -> Arc<RwLock<CustomMetrics>> {
self.custom_metrics.clone()
}
pub fn alerting_system(&self) -> Option<&AlertingSystem> {
self.alerting_system.as_ref()
}
pub async fn record_script_execution(
&self,
script_name: &str,
duration: std::time::Duration,
success: bool,
) {
let metrics: _ = self.custom_metrics.write().await;
metrics.record_script_execution(duration, success).await;
if let Some(logger) = &self.structured_logger {
let context: _ = HashMap::from([
(
"script_name".to_string(),
Value::String(script_name.to_string()),
),
(
"duration_ms".to_string(),
Value::Number(serde_json::Number::from(duration.as_millis() as u64)),
),
("success".to_string(), Value::Bool(success)),
]);
if success {
logger.info("Script executed successfully", context).await;
} else {
logger.warn("Script execution failed", context).await;
}
}
}
pub async fn record_memory_usage(&self, bytes: usize) {
let metrics: _ = self.custom_metrics.write().await;
metrics.record_memory_usage(bytes).await;
}
pub async fn record_network_io(
&self,
operation: &str,
bytes: usize,
duration: std::time::Duration,
) {
let metrics: _ = self.custom_metrics.write().await;
metrics.record_network_io(operation, bytes, duration).await;
}
pub async fn get_metrics(&self) -> ObservableMetrics {
let custom_metrics: _ = self.custom_metrics.read().await;
ObservableMetrics {
runtime: custom_metrics.runtime_metrics().await.clone(),
performance: custom_metrics.performance_metrics().await.clone(),
business: custom_metrics.business_metrics().await.clone(),
}
}
pub async fn shutdown(&self) -> Result<()> {
info!("Shutting down observability system...");
if let Some(alerting) = &self.alerting_system {
alerting.shutdown().await?;
}
if let Some(exporter) = &self.prometheus_exporter {
exporter.write().await.shutdown().await?;
}
info!("Observability system shutdown complete");
Ok(())
}
}
pub struct ObservableMetrics {
pub runtime: std::sync::Arc<tokio::sync::RwLock<RuntimeMetrics>>,
pub performance: std::sync::Arc<tokio::sync::RwLock<PerformanceMetrics>>,
pub business: std::sync::Arc<tokio::sync::RwLock<BusinessMetrics>>,
}
#[derive(Debug, Clone)]
pub struct RuntimeMetricsSnapshot {
pub active_scripts: u64,
pub memory_usage_bytes: u64,
pub cpu_usage_percent: f64,
}
#[derive(Debug, Clone)]
pub struct PerformanceMetricsSnapshot {
pub avg_script_duration_ms: f64,
pub p95_script_duration_ms: f64,
pub jit_compilation_time_ms: f64,
pub gc_pause_time_ms: f64,
pub network_latency_ms: f64,
}
#[derive(Debug, Clone)]
pub struct BusinessMetricsSnapshot {
pub total_scripts_executed: u64,
pub total_packages_loaded: u64,
pub total_hot_reloads: u64,
pub error_rate_percent: f64,
}
#[cfg(test)]
mod tests {
#[tokio::test]
async fn test_observability_system_creation() {
let config: _ = ObservabilityConfig::default();
let system: _ = ObservableSystem::new(config).await;
assert!(system.is_ok());
}
#[tokio::test]
async fn test_record_script_execution() {
let config: _ = ObservabilityConfig::default();
let system: _ = ObservableSystem::new(config).await.unwrap();
system
.record_script_execution("test.js", std::time::Duration::from_millis(100), true)
.await;
}
}