mod counter;
mod gauge;
mod histogram;
mod labels;
mod registry;
pub use counter::Counter;
pub use gauge::Gauge;
pub use histogram::{Histogram, DEFAULT_BUCKETS, PROMETHEUS_BUCKETS};
pub use labels::{LabelPair, LabelValidator, Labels};
pub use registry::{Registry, RegistryError};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MetricDesc {
pub name: String,
pub labels: Labels,
}
impl MetricDesc {
pub fn new(name: impl Into<String>, labels: Labels) -> Self {
Self { name: name.into(), labels }
}
pub fn key(&self) -> String {
let mut key = self.name.clone();
let mut sorted_labels: Vec<_> = self.labels.iter().collect();
sorted_labels.sort_by_key(|(k, _)| *k);
for (k, v) in sorted_labels {
key.push_str(&format!("|{}={}", k, v));
}
key
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MetricType {
Counter,
Gauge,
Histogram,
}
impl std::fmt::Display for MetricType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MetricType::Counter => write!(f, "counter"),
MetricType::Gauge => write!(f, "gauge"),
MetricType::Histogram => write!(f, "histogram"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metric_desc_key() {
let labels: Labels = [("method", "GET"), ("status", "200")]
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
let desc = MetricDesc::new("http_requests", labels);
assert_eq!(desc.key(), "http_requests|method=GET|status=200");
}
#[test]
fn test_metric_desc_key_empty_labels() {
let desc = MetricDesc::new("simple_metric", Labels::new());
assert_eq!(desc.key(), "simple_metric");
}
}
static_assertions::assert_impl_all!(Counter: Send, Sync);
static_assertions::assert_impl_all!(Gauge: Send, Sync);
static_assertions::assert_impl_all!(Histogram: Send, Sync);
static_assertions::assert_impl_all!(Registry: Send, Sync);
static_assertions::assert_impl_all!(MetricDesc: Send, Sync);
static_assertions::assert_impl_all!(MetricType: Send, Sync);
static_assertions::assert_impl_all!(LabelValidator: Send, Sync);
#[cfg(kani)]
mod verification {
use super::*;
#[kani::proof]
fn verify_key_starts_with_name() {
let name = String::from("test_metric");
let desc = MetricDesc::new(name.clone(), Labels::new());
let key = desc.key();
assert!(key.starts_with(&name));
}
#[kani::proof]
fn verify_empty_labels_key_equals_name() {
let name = String::from("metric");
let desc = MetricDesc::new(name.clone(), Labels::new());
assert_eq!(desc.key(), name);
}
#[kani::proof]
fn verify_metric_type_display() {
let counter_str = format!("{}", MetricType::Counter);
let gauge_str = format!("{}", MetricType::Gauge);
let histogram_str = format!("{}", MetricType::Histogram);
assert_eq!(counter_str, "counter");
assert_eq!(gauge_str, "gauge");
assert_eq!(histogram_str, "histogram");
}
}