use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct PoolMetrics {
pub total_retrieved: usize,
pub total_returned: usize,
pub active_objects: usize,
pub available_objects: usize,
pub pool_empty_events: usize,
pub validation_failures: usize,
pub queue_push_failures: usize,
pub total_detached: usize,
pub utilization: f64,
pub max_capacity: usize,
}
impl PoolMetrics {
pub fn export(&self) -> HashMap<String, String> {
let mut metrics = HashMap::new();
metrics.insert("total_retrieved".to_string(), self.total_retrieved.to_string());
metrics.insert("total_returned".to_string(), self.total_returned.to_string());
metrics.insert("active_objects".to_string(), self.active_objects.to_string());
metrics.insert("available_objects".to_string(), self.available_objects.to_string());
metrics.insert("pool_empty_events".to_string(), self.pool_empty_events.to_string());
metrics.insert("validation_failures".to_string(), self.validation_failures.to_string());
metrics.insert("queue_push_failures".to_string(), self.queue_push_failures.to_string());
metrics.insert("total_detached".to_string(), self.total_detached.to_string());
metrics.insert("utilization".to_string(), format!("{:.2}", self.utilization));
metrics.insert("max_capacity".to_string(), self.max_capacity.to_string());
metrics
}
}
pub struct MetricsExporter;
impl MetricsExporter {
pub fn export_prometheus(
metrics: &PoolMetrics,
pool_name: &str,
tags: Option<&HashMap<String, String>>,
) -> String {
let mut output = String::new();
let labels = Self::format_labels(pool_name, tags);
output.push_str("# HELP objectpool_objects_active Current active objects\n");
output.push_str("# TYPE objectpool_objects_active gauge\n");
output.push_str(&format!("objectpool_objects_active{{{}}} {}\n", labels, metrics.active_objects));
output.push_str("# HELP objectpool_objects_available Current available objects\n");
output.push_str("# TYPE objectpool_objects_available gauge\n");
output.push_str(&format!("objectpool_objects_available{{{}}} {}\n", labels, metrics.available_objects));
output.push_str("# HELP objectpool_utilization Pool utilization ratio\n");
output.push_str("# TYPE objectpool_utilization gauge\n");
output.push_str(&format!("objectpool_utilization{{{}}} {:.2}\n", labels, metrics.utilization));
output.push_str("# HELP objectpool_objects_retrieved_total Total objects retrieved\n");
output.push_str("# TYPE objectpool_objects_retrieved_total counter\n");
output.push_str(&format!("objectpool_objects_retrieved_total{{{}}} {}\n", labels, metrics.total_retrieved));
output.push_str("# HELP objectpool_objects_returned_total Total objects returned\n");
output.push_str("# TYPE objectpool_objects_returned_total counter\n");
output.push_str(&format!("objectpool_objects_returned_total{{{}}} {}\n", labels, metrics.total_returned));
output.push_str("# HELP objectpool_events_empty_total Pool empty events\n");
output.push_str("# TYPE objectpool_events_empty_total counter\n");
output.push_str(&format!("objectpool_events_empty_total{{{}}} {}\n", labels, metrics.pool_empty_events));
output.push_str("# HELP objectpool_validation_failures_total Validation failures\n");
output.push_str("# TYPE objectpool_validation_failures_total counter\n");
output.push_str(&format!("objectpool_validation_failures_total{{{}}} {}\n", labels, metrics.validation_failures));
output.push_str("# HELP objectpool_queue_push_failures_total Queue push failures causing object drops\n");
output.push_str("# TYPE objectpool_queue_push_failures_total counter\n");
output.push_str(&format!("objectpool_queue_push_failures_total{{{}}} {}\n", labels, metrics.queue_push_failures));
output.push_str("# HELP objectpool_objects_detached_total Objects permanently detached via into_detached()\n");
output.push_str("# TYPE objectpool_objects_detached_total counter\n");
output.push_str(&format!("objectpool_objects_detached_total{{{}}} {}\n", labels, metrics.total_detached));
output
}
fn format_labels(pool_name: &str, tags: Option<&HashMap<String, String>>) -> String {
let mut labels = vec![format!("pool=\"{}\"", pool_name)];
if let Some(tags) = tags {
for (key, value) in tags {
labels.push(format!("{}=\"{}\"", key, value));
}
}
labels.join(",")
}
}
pub(crate) struct MetricsTracker {
pub total_retrieved: Arc<AtomicUsize>,
pub total_returned: Arc<AtomicUsize>,
pub pool_empty_events: Arc<AtomicUsize>,
pub validation_failures: Arc<AtomicUsize>,
pub queue_push_failures: Arc<AtomicUsize>,
pub total_detached: Arc<AtomicUsize>,
}
impl MetricsTracker {
pub fn new() -> Self {
Self {
total_retrieved: Arc::new(AtomicUsize::new(0)),
total_returned: Arc::new(AtomicUsize::new(0)),
pool_empty_events: Arc::new(AtomicUsize::new(0)),
validation_failures: Arc::new(AtomicUsize::new(0)),
queue_push_failures: Arc::new(AtomicUsize::new(0)),
total_detached: Arc::new(AtomicUsize::new(0)),
}
}
pub fn get_metrics(&self, active: usize, available: usize, capacity: usize) -> PoolMetrics {
let utilization = if capacity > 0 {
active as f64 / capacity as f64
} else {
0.0
};
PoolMetrics {
total_retrieved: self.total_retrieved.load(Ordering::Relaxed),
total_returned: self.total_returned.load(Ordering::Relaxed),
active_objects: active,
available_objects: available,
pool_empty_events: self.pool_empty_events.load(Ordering::Relaxed),
validation_failures: self.validation_failures.load(Ordering::Relaxed),
queue_push_failures: self.queue_push_failures.load(Ordering::Relaxed),
total_detached: self.total_detached.load(Ordering::Relaxed),
utilization,
max_capacity: capacity,
}
}
}
impl Default for MetricsTracker {
fn default() -> Self {
Self::new()
}
}