use std::collections::{BTreeMap, HashMap};
use std::sync::{Arc, Mutex};
use serde::{Serialize, Deserialize};
use tracing::{info, warn, error, debug, instrument};
use chrono::{DateTime, Utc};
use std::sync::RwLock;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum Metric {
Counter {
name: String,
value: u64,
labels: std::collections::HashMap<String, String>,
timestamp: DateTime<Utc>,
},
Gauge {
name: String,
value: f64,
labels: std::collections::HashMap<String, String>,
timestamp: DateTime<Utc>,
},
Histogram {
name: String,
values: Vec<f64>,
labels: std::collections::HashMap<String, String>,
timestamp: DateTime<Utc>,
},
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ClusterMetrics {
pub cluster_name: String,
pub namespace: String,
pub tenant_id: Option<String>,
pub cpu_usage: f64,
pub memory_usage_mb: u64,
pub memory_limit_mb: u64,
pub cpu_limit_cores: f64,
pub replicas: u32,
pub ready_replicas: u32,
pub restart_count: u32,
pub uptime_seconds: u64,
pub request_count: u64,
pub error_count: u64,
pub average_response_time_ms: f64,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TenantMetrics {
pub tenant_id: String,
pub tenant_name: String,
pub active_clusters: u32,
pub total_replicas: u32,
pub total_memory_usage_mb: u64,
pub total_cpu_usage_cores: f64,
pub total_storage_usage_gb: u64,
pub concurrent_executions: u32,
pub monthly_request_count: u64,
pub monthly_error_count: u64,
pub cost_estimate_usd: f64,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SystemMetrics {
pub total_clusters: u32,
pub active_clusters: u32,
pub total_tenants: u32,
pub active_tenants: u32,
pub total_requests_last_hour: u64,
pub total_errors_last_hour: u64,
pub system_cpu_usage: f64,
pub system_memory_usage_mb: u64,
pub system_memory_limit_mb: u64,
pub network_io_bytes: u64,
pub disk_io_bytes: u64,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Alert {
pub id: String,
pub name: String,
pub description: String,
pub severity: AlertSeverity,
pub condition: AlertCondition,
pub labels: std::collections::HashMap<String, String>,
pub annotations: std::collections::HashMap<String, String>,
pub enabled: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum AlertSeverity {
Critical,
High,
Medium,
Low,
Info,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum AlertCondition {
Threshold {
metric: String,
operator: ComparisonOperator,
value: f64,
duration_seconds: u64,
},
Rate {
metric: String,
period_seconds: u64,
threshold: f64,
},
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum ComparisonOperator {
GreaterThan,
GreaterThanOrEqual,
LessThan,
LessThanOrEqual,
Equal,
NotEqual,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct AlertEvent {
pub alert_id: String,
pub alert_name: String,
pub severity: AlertSeverity,
pub status: AlertStatus,
pub description: String,
pub labels: std::collections::HashMap<String, String>,
pub timestamp: DateTime<Utc>,
pub resolved_at: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub enum AlertStatus {
Firing,
Resolved,
}
#[derive(Debug, Clone)]
pub struct MonitoringConfig {
pub metrics_retention_hours: u64,
pub metrics_collection_interval_seconds: u64,
pub alerts_enabled: bool,
pub prometheus_endpoint: Option<String>,
pub grafana_endpoint: Option<String>,
}
pub struct MonitoringManager {
config: MonitoringConfig,
metrics: Arc<RwLock<std::collections::HashMap<String, Vec<Metric>>>>,
cluster_metrics: Arc<RwLock<std::collections::HashMap<String, Vec<ClusterMetrics>>>>,
tenant_metrics: Arc<RwLock<std::collections::HashMap<String, Vec<TenantMetrics>>>>,
system_metrics: Arc<RwLock<Vec<SystemMetrics>>>,
alerts: Arc<RwLock<std::collections::HashMap<String, Alert>>>,
active_alerts: Arc<RwLock<std::collections::HashMap<String, AlertEvent>>>,
}
impl MonitoringManager {
pub fn new(config: MonitoringConfig) -> Self {
MonitoringManager {
config,
metrics: Arc::new(Mutex::new(std::collections::HashMap::new()))
cluster_metrics: Arc::new(Mutex::new(std::collections::HashMap::new()))
tenant_metrics: Arc::new(Mutex::new(std::collections::HashMap::new()))
system_metrics: Arc::new(Mutex::new(Vec::new()))
alerts: Arc::new(Mutex::new(std::collections::HashMap::new()))
active_alerts: Arc::new(Mutex::new(std::collections::HashMap::new()))
}
}
pub async fn start(&self) -> Result<(), Box<dyn std::error::Error>> {
info!("Starting Enterprise Monitoring Manager");
let cluster_metrics: _ = self.cluster_metrics.clone();
let tenant_metrics: _ = self.tenant_metrics.clone();
let system_metrics: _ = self.system_metrics.clone();
tokio::spawn(async move {
let mut interval = tokio::time::interval(std::time::Duration::from_secs(30));
loop {
interval.tick().await;
debug!("Collecting metrics");
Self::collect_system_metrics(&system_metrics).await;
}
});
if self.config.alerts_enabled {
let alerts: _ = self.alerts.clone();
let active_alerts: _ = self.active_alerts.clone();
tokio::spawn(async move {
let mut interval = tokio::time::interval(std::time::Duration::from_secs(10));
loop {
interval.tick().await;
debug!("Evaluating alerts");
Self::evaluate_alerts(&alerts, &active_alerts).await;
}
});
}
info!("Enterprise Monitoring Manager started successfully");
Ok(())
}
#[instrument(skip(self))]
pub async fn record_metric(&self, metric: Metric) {
let name: _ = match &metric {
Metric::Counter { name, .. } => name.clone(),
Metric::Gauge { name, .. } => name.clone(),
Metric::Histogram { name, .. } => name.clone(),
};
let mut metrics = self.metrics.write().await;
metrics.entry(name).or_insert_with(Vec::new).push(metric);
info!("Recorded metric: {}", name);
}
#[instrument(skip(self))]
pub async fn record_cluster_metrics(&self, metrics: ClusterMetrics) {
let mut cluster_metrics = self.cluster_metrics.write().await;
let key: _ = format!("{}/{}, metrics.namespace", metrics.cluster_name));
cluster_metrics.entry(key).or_insert_with(Vec::new).push(metrics);
info!("Recorded cluster metrics for: {}", key);
}
#[instrument(skip(self))]
pub async fn record_tenant_metrics(&self, metrics: TenantMetrics) {
let mut tenant_metrics = self.tenant_metrics.write().await;
tenant_metrics
.entry(metrics.tenant_id.clone())
.or_insert_with(Vec::new)
.push(metrics);
info!("Recorded tenant metrics for: {}", metrics.tenant_id);
}
pub async fn get_cluster_metrics(
&self,
cluster_name: &str,
namespace: &str,
limit: Option<usize>,
) -> Result<Vec<ClusterMetrics>, Box<dyn std::error::Error>> {
let cluster_metrics: _ = self.cluster_metrics.read().await;
let key: _ = format!("{}/{}, namespace", cluster_name));
if let Some(metrics) = cluster_metrics.get(&key) {
let metrics: _ = metrics.clone();
let metrics: _ = if let Some(limit) = limit {
metrics.into_iter().rev().take(limit).collect()
} else {
metrics
};
Ok(metrics.into_iter().rev().collect())
} else {
Ok(Vec::new())
}
}
pub async fn get_tenant_metrics(
&self,
tenant_id: &str,
limit: Option<usize>,
) -> Result<Vec<TenantMetrics>, Box<dyn std::error::Error>> {
let tenant_metrics: _ = self.tenant_metrics.read().await;
if let Some(metrics) = tenant_metrics.get(tenant_id) {
let metrics: _ = metrics.clone();
let metrics: _ = if let Some(limit) = limit {
metrics.into_iter().rev().take(limit).collect()
} else {
metrics
};
Ok(metrics.into_iter().rev().collect())
} else {
Ok(Vec::new())
}
}
pub async fn get_system_metrics(
&self,
limit: Option<usize>,
) -> Result<Vec<SystemMetrics>, Box<dyn std::error::Error>> {
let system_metrics: _ = self.system_metrics.read().await;
let metrics: _ = if let Some(limit) = limit {
system_metrics.iter().rev().take(limit).cloned().collect()
} else {
system_metrics.iter().rev().cloned().collect()
};
Ok(metrics)
}
pub async fn create_alert(&self, alert: Alert) -> Result<(), Box<dyn std::error::Error>> {
let mut alerts = self.alerts.write().await;
alerts.insert(alert.id.clone(), alert);
info!("Created alert: {}", alert.name);
Ok(())
}
pub async fn get_active_alerts(&self) -> Vec<AlertEvent> {
let active_alerts: _ = self.active_alerts.read().await;
active_alerts.values().cloned().collect()
}
pub async fn export_prometheus_metrics(&self) -> Result<String, Box<dyn std::error::Error>> {
let metrics: _ = self.metrics.read().await;
let mut output = String::new();
output.push_str(&format!("# Generated at {}\n\n", Utc::now());
for (name, metric_list) in metrics.iter() {
for metric in metric_list {
match metric {
Metric::Counter { value, labels, .. } => {
output.push_str(&format!("{} {{", name));
for (key, value) in labels {
output.push_str(&format!(" {}=\"{}\"", key, value));
}
output.push_str(&format!(" }} {}\n", value));
}
Metric::Gauge { value, labels, .. } => {
output.push_str(&format!("{} {{", name));
for (key, value) in labels {
output.push_str(&format!(" {}=\"{}\"", key, value));
}
output.push_str(&format!(" }} {}\n", value));
}
Metric::Histogram { values, labels, .. } => {
if !values.is_empty() {
let sum: f64 = values.iter().sum();
let count: _ = values.len() as f64;
let avg: _ = sum / count;
output.push_str(&format!("{} {{", name));
for (key, value) in labels {
output.push_str(&format!(" {}=\"{}\"", key, value));
}
output.push_str(&format!("_sum }} {}\n", sum));
output.push_str(&format!("{} {{", name));
for (key, value) in labels {
output.push_str(&format!(" {}=\"{}\"", key, value));
}
output.push_str(&format!("_count }} {}\n", count));
output.push_str(&format!("{} {{", name));
for (key, value) in labels {
output.push_str(&format!(" {}=\"{}\"", key, value));
}
output.push_str(&format!("_avg }} {}\n", avg));
}
}
}
}
}
Ok(output)
}
async fn collect_system_metrics(system_metrics: &Arc<RwLock<Vec<SystemMetrics>) {
let metrics: _ = SystemMetrics {
total_clusters: 10,
active_clusters: 8,
total_tenants: 5,
active_tenants: 4,
total_requests_last_hour: 1000,
total_errors_last_hour: 10,
system_cpu_usage: 50.0,
system_memory_usage_mb: 4096,
system_memory_limit_mb: 8192,
network_io_bytes: 1024 * 1024,
disk_io_bytes: 10 * 1024 * 1024,
timestamp: Utc::now(),
};
let mut system_metrics_write = system_metrics.write().await;
system_metrics_write.push(metrics);
}
async fn evaluate_alerts(
alerts: &Arc<RwLock<std::collections::HashMap<String, Alert>>>,
active_alerts: &Arc<RwLock<std::collections::HashMap<String, AlertEvent>>>,
) {
let alerts_read: _ = alerts.read().await;
let mut active_alerts_write = active_alerts.write().await;
for alert in alerts_read.values() {
if !alert.enabled {
continue;
}
let should_fire: _ = match &alert.condition {
AlertCondition::Threshold { metric, operator, value, .. } => {
false }
AlertCondition::Rate { threshold, .. } => {
false }
};
if should_fire {
if !active_alerts_write.contains_key(&alert.id) {
let event: _ = AlertEvent {
alert_id: alert.id.clone(),
alert_name: alert.name.clone(),
severity: alert.severity.clone(),
status: AlertStatus::Firing,
description: alert.description.clone(),
labels: alert.labels.clone(),
timestamp: Utc::now(),
resolved_at: None,
};
active_alerts_write.insert(alert.id.clone(), event);
warn!("Alert fired: {}", alert.name);
}
} else {
if let Some(event) = active_alerts_write.get_mut(&alert.id) {
if event.status == AlertStatus::Firing {
event.status = AlertStatus::Resolved;
event.resolved_at = Some(Utc::now());
info!("Alert resolved: {}", alert.name);
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[tokio::test]
async fn test_metric_recording() {
let config: _ = MonitoringConfig {
metrics_retention_hours: 24,
metrics_collection_interval_seconds: 30,
alerts_enabled: true,
prometheus_endpoint: None,
grafana_endpoint: None,
};
let manager: _ = MonitoringManager::new(config);
let metric: _ = Metric::Counter {
name: "amberjs_requests_total".to_string(),
value: 100,
labels: {
let mut labels = std::collections::HashMap::new();
labels.insert("method".to_string(), "GET".to_string());
labels.insert("status".to_string(), "200".to_string());
labels
},
timestamp: Utc::now(),
};
manager.record_metric(metric).await;
let prometheus_output: _ = manager.export_prometheus_metrics().await.unwrap();
assert!(prometheus_output.contains("amberjs_requests_total"));
assert!(prometheus_output.contains("method=\"GET\""));
}
#[tokio::test]
async fn test_cluster_metrics() {
let config: _ = MonitoringConfig {
metrics_retention_hours: 24,
metrics_collection_interval_seconds: 30,
alerts_enabled: false,
prometheus_endpoint: None,
grafana_endpoint: None,
};
let manager: _ = MonitoringManager::new(config);
let metrics: _ = ClusterMetrics {
cluster_name: "test-cluster".to_string(),
namespace: "default".to_string(),
tenant_id: Some("tenant-1".to_string()),
cpu_usage: 50.0,
memory_usage_mb: 1024,
memory_limit_mb: 2048,
cpu_limit_cores: 2.0,
replicas: 3,
ready_replicas: 3,
restart_count: 0,
uptime_seconds: 3600,
request_count: 1000,
error_count: 5,
average_response_time_ms: 100.0,
timestamp: Utc::now(),
};
manager.record_cluster_metrics(metrics.clone()).await;
let retrieved_metrics: _ = manager
.get_cluster_metrics("test-cluster", "default", Some(10))
.await
.unwrap();
assert_eq!(retrieved_metrics.len(), 1);
assert_eq!(retrieved_metrics[0].cluster_name, "test-cluster");
assert_eq!(retrieved_metrics[0].namespace, "default");
}
}