use std::sync::Arc;
use prometheus::{CounterVec, GaugeVec, HistogramVec};
use crate::metrics::registry::MetricsRegistry;
#[derive(Debug, Clone)]
pub struct ServiceMetrics {
registry: Arc<MetricsRegistry>,
service_name: String,
pub requests_total: CounterVec,
pub request_duration: HistogramVec,
pub active_requests: GaugeVec,
pub success_count: CounterVec,
pub error_count: CounterVec,
pub errors_total: CounterVec,
}
impl ServiceMetrics {
pub fn new(service_name: impl Into<String>) -> Self {
let service_name_str = service_name.into();
let registry = Arc::new(MetricsRegistry::new(&service_name_str));
let requests_total = registry
.register_counter_vec(
"requests_total",
"Total number of requests",
&["service", "method"],
)
.unwrap();
let request_duration = registry
.register_histogram_vec(
"request_duration_seconds",
"Request duration in seconds",
None,
&["service", "method"],
)
.unwrap();
let active_requests = registry
.register_gauge_vec(
"active_requests",
"Number of currently active requests",
&["service", "method"],
)
.unwrap();
let success_count = registry
.register_counter_vec(
"success_count",
"Total number of successful requests",
&["service", "method"],
)
.unwrap();
let error_count = registry
.register_counter_vec(
"error_count",
"Total number of errors",
&["service", "method", "error_code"],
)
.unwrap();
let errors_total = registry
.register_counter_vec(
"errors_total",
"Total number of errors (legacy)",
&["method", "path", "error_type"],
)
.unwrap();
Self {
registry,
service_name: service_name_str,
requests_total,
request_duration,
active_requests,
success_count,
error_count,
errors_total,
}
}
pub fn registry(&self) -> &Arc<MetricsRegistry> {
&self.registry
}
pub fn service_name(&self) -> &str {
&self.service_name
}
pub fn increment_requests(&self, method: &str, _path: &str, _status: &str) {
self.requests_total
.with_label_values(&[self.service_name.as_str(), method])
.inc();
}
pub fn record_duration(&self, method: &str, _path: &str, duration: std::time::Duration) {
self.request_duration
.with_label_values(&[self.service_name.as_str(), method])
.observe(duration.as_secs_f64());
}
pub fn increment_active(&self, method: &str) {
self.active_requests
.with_label_values(&[self.service_name.as_str(), method])
.inc();
}
pub fn decrement_active(&self, method: &str) {
self.active_requests
.with_label_values(&[self.service_name.as_str(), method])
.dec();
}
pub fn increment_errors(&self, method: &str, path: &str, error_type: &str) {
self.errors_total
.with_label_values(&[method, path, error_type])
.inc();
}
pub async fn gather_and_encode(&self) -> Result<String, prometheus::Error> {
self.registry.gather_and_encode().await
}
}
impl Default for ServiceMetrics {
fn default() -> Self {
Self::new("sunbeam-g2v")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_service_metrics_new() {
let metrics = ServiceMetrics::new("test-service");
assert_eq!(metrics.service_name(), "test-service");
}
#[test]
fn test_service_metrics_default() {
let metrics = ServiceMetrics::default();
assert_eq!(metrics.service_name(), "sunbeam-g2v");
}
#[test]
fn test_service_metrics_increment_requests() {
let metrics = ServiceMetrics::new("test-service");
metrics.increment_requests("GET", "/health", "200");
}
#[tokio::test]
async fn test_service_metrics_gather() {
let metrics = ServiceMetrics::new("test-service");
metrics.increment_requests("GET", "/test", "200");
let encoded = metrics.gather_and_encode().await.unwrap();
assert!(!encoded.is_empty());
}
}