#![allow(non_snake_case)]
use std::collections::HashMap as FxHashMap;
use std::sync::{Arc, RwLock};
#[cfg(feature = "observability")]
use prometheus::{Counter, Gauge, Histogram, Registry, Encoder, TextEncoder};
use crate::core::RiResult;
use crate::core::lock::RwLockExtensions;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct RiPrometheusExporter {
registry: Arc<Registry>,
counters: Arc<RwLock<FxHashMap<String, Counter>>>,
gauges: Arc<RwLock<FxHashMap<String, Gauge>>>,
histograms: Arc<RwLock<FxHashMap<String, Histogram>>>,
}
#[allow(dead_code)]
impl RiPrometheusExporter {
pub fn new() -> RiResult<Self> {
let registry = Arc::new(Registry::new());
Ok(RiPrometheusExporter {
registry: registry.clone(),
counters: Arc::new(RwLock::new(FxHashMap::default())),
gauges: Arc::new(RwLock::new(FxHashMap::default())),
histograms: Arc::new(RwLock::new(FxHashMap::default())),
})
}
pub fn register_counter(&self, name: &str, help: &str) -> RiResult<()> {
let counter = Counter::new(name, help)?;
self.registry.register(Box::new(counter.clone()))?;
let mut counters = self.counters.write_safe("counters for register")?;
counters.insert(name.to_string(), counter);
Ok(())
}
pub fn increment_counter(&self, name: &str, value: f64) -> RiResult<()> {
let counters = self.counters.read_safe("counters for increment")?;
if let Some(counter) = counters.get(name) {
counter.inc_by(value);
Ok(())
} else {
Err(crate::core::RiError::Io(format!("Counter {name} not found")))
}
}
pub fn register_gauge(&self, name: &str, help: &str) -> RiResult<()> {
let gauge = Gauge::new(name, help)?;
self.registry.register(Box::new(gauge.clone()))?;
let mut gauges = self.gauges.write_safe("gauges for register")?;
gauges.insert(name.to_string(), gauge);
Ok(())
}
pub fn set_gauge(&self, name: &str, value: f64) -> RiResult<()> {
let gauges = self.gauges.read_safe("gauges for set")?;
if let Some(gauge) = gauges.get(name) {
gauge.set(value);
Ok(())
} else {
Err(crate::core::RiError::Io(format!("Gauge {name} not found")))
}
}
pub fn register_histogram(&self, name: &str, help: &str, buckets: Vec<f64>) -> RiResult<()> {
let histogram = Histogram::with_opts(prometheus::HistogramOpts::new(name, help).buckets(buckets))?;
self.registry.register(Box::new(histogram.clone()))?;
let mut histograms = self.histograms.write_safe("histograms for register")?;
histograms.insert(name.to_string(), histogram);
Ok(())
}
pub fn observe_histogram(&self, name: &str, value: f64) -> RiResult<()> {
let histograms = self.histograms.read_safe("histograms for observe")?;
if let Some(histogram) = histograms.get(name) {
histogram.observe(value);
Ok(())
} else {
Err(crate::core::RiError::Io(format!("Histogram {name} not found")))
}
}
pub fn render(&self) -> RiResult<String> {
let encoder = TextEncoder::new();
let metric_families = self.registry.gather();
let mut buffer = Vec::with_capacity(1024);
encoder.encode(&metric_families, &mut buffer)?;
String::from_utf8(buffer).map_err(|e| crate::core::RiError::Serde(format!("Invalid UTF-8 in Prometheus output: {}", e)))
}
pub fn add_counter_panel(&self, dashboard: &mut crate::observability::grafana::RiGrafanaDashboard, title: &str, query: &str) -> RiResult<()> {
let mut generator = crate::observability::grafana::RiGrafanaDashboardGenerator::new();
let mut panel = generator.create_panel(title, "stat", 0, 0, 12, 8);
panel.targets.push(generator.create_prometheus_target(query, "A", None));
dashboard.panels.push(panel);
Ok(())
}
pub fn add_gauge_panel(&self, dashboard: &mut crate::observability::grafana::RiGrafanaDashboard, title: &str, query: &str) -> RiResult<()> {
let mut generator = crate::observability::grafana::RiGrafanaDashboardGenerator::new();
let mut panel = generator.create_panel(title, "gauge", 12, 0, 12, 8);
panel.targets.push(generator.create_prometheus_target(query, "A", None));
dashboard.panels.push(panel);
Ok(())
}
pub fn add_stat_panel(&self, dashboard: &mut crate::observability::grafana::RiGrafanaDashboard, title: &str, query: &str) -> RiResult<()> {
let mut generator = crate::observability::grafana::RiGrafanaDashboardGenerator::new();
let mut panel = generator.create_panel(title, "stat", 0, 8, 12, 8);
panel.targets.push(generator.create_prometheus_target(query, "A", None));
dashboard.panels.push(panel);
Ok(())
}
pub fn generate_dashboard(&self, title: &str) -> RiResult<crate::observability::grafana::RiGrafanaDashboard> {
let mut dashboard = crate::observability::grafana::RiGrafanaDashboard::new(title);
self.add_counter_panel(&mut dashboard, "Request Count", "dms_requests_total")?;
self.add_gauge_panel(&mut dashboard, "Active Connections", "dms_active_connections")?;
self.add_stat_panel(&mut dashboard, "Response Time", "dms_response_time_seconds")?;
Ok(dashboard)
}
pub fn generate_default_dashboard(&self) -> RiResult<crate::observability::grafana::RiGrafanaDashboard> {
self.generate_dashboard("Ri Metrics Dashboard")
}
}