use std::time::Duration;
use prometheus::Encoder as _;
#[prometric_derive::metrics(scope = "app")]
struct AppMetrics {
#[metric(rename = "http_requests_total", labels = ["method", "path"])]
http_requests: prometric::Counter,
#[metric(labels = ["method", "path"], buckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0])]
http_requests_duration: prometric::Histogram,
#[metric(rename = "current_active_users", labels = ["service"], help = "The current number of active users.")]
current_users: prometric::Gauge<u64>,
#[metric(rename = "account_balance", labels = ["account_id"])]
account_balance: prometric::Gauge<f64>,
#[metric]
errors: prometric::Counter,
}
#[test]
fn test_macro() {
let registry = prometheus::default_registry();
let app_metrics = AppMetrics::builder()
.with_registry(registry)
.with_label("host", "localhost") .with_label("port", "8080")
.build();
app_metrics.http_requests("GET", "/").inc();
app_metrics.http_requests("GET", "/").inc();
app_metrics.http_requests("POST", "/").inc_by(2);
app_metrics.current_users("service-1").set(10);
app_metrics.current_users("service-1").set(20);
let duration = Duration::from_secs(1);
app_metrics.http_requests_duration("GET", "/").observe(duration.as_secs_f64());
app_metrics.account_balance("1234567890").set(-12.2);
app_metrics.errors().inc();
let encoder = prometheus::TextEncoder::new();
let metric_families = registry.gather();
let mut buffer = vec![];
encoder.encode(&metric_families, &mut buffer).unwrap();
let output = String::from_utf8(buffer).unwrap();
println!("\n=== Prometheus Metrics Output ===\n{output}");
assert!(output.contains("app_current_active_users"));
assert!(output.contains("app_http_requests_duration"));
assert!(output.contains("app_http_requests_total"));
assert!(output.contains("app_errors"));
assert!(output.contains("app_account_balance"));
assert!(output.contains("The current number of active users."));
}
#[test]
fn test_autocasts() {
let registry = prometheus::Registry::new();
let app_metrics =
AppMetrics::builder().with_registry(®istry).with_label("host", "localhost").build();
app_metrics.http_requests("GET", "/").inc_by(3); app_metrics.http_requests("GET", "/").inc_by(3u32);
app_metrics.http_requests("GET", "/").inc_by(3i32);
app_metrics.http_requests("GET", "/").inc_by(3u64);
app_metrics.http_requests("GET", "/").inc_by(3usize);
app_metrics.current_users("service-1").set(3); app_metrics.current_users("service-1").set(3u32);
app_metrics.current_users("service-1").set(3i32);
app_metrics.current_users("service-1").set(3usize);
app_metrics.http_requests_duration("GET", "/").observe(3); app_metrics.http_requests_duration("GET", "/").observe(3u32);
app_metrics.http_requests_duration("GET", "/").observe(3i32);
app_metrics.http_requests_duration("GET", "/").observe(3f32);
app_metrics.http_requests_duration("GET", "/").observe(3f64);
app_metrics.http_requests_duration("GET", "/").observe(3usize);
}
#[test]
fn test_double_registration_success() {
let registry = prometheus::Registry::new();
AppMetrics::builder().with_registry(®istry).with_label("host", "localhost").build();
AppMetrics::builder().with_registry(®istry).with_label("host", "0.0.0.0").build();
}
#[prometric_derive::metrics(scope = "test", static)]
struct TestMetrics {
#[metric(labels = ["label1"])]
test_counter: prometric::Counter,
#[metric]
test_gauge: prometric::Gauge,
}
#[test]
fn test_static() {
TEST_METRICS.test_counter("value1").inc();
TEST_METRICS.test_gauge().set(42);
let registry = prometheus::default_registry();
let metric_families = registry.gather();
let encoder = prometheus::TextEncoder::new();
let mut buffer = vec![];
encoder.encode(&metric_families, &mut buffer).unwrap();
let output = String::from_utf8(buffer).unwrap();
assert!(output.contains("test_test_counter"));
assert!(output.contains("test_test_gauge"));
TEST_METRICS.test_counter("value1").inc();
TEST_METRICS.test_gauge().inc();
}
#[test]
fn bucket_expressions_work() {
const BUCKETS: &[f64] = &[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0];
fn buckets() -> &'static [f64] {
BUCKETS
}
#[prometric_derive::metrics(scope = "test")]
struct BucketMetrics {
#[metric(buckets = buckets())]
hist: prometric::Histogram,
}
let registry = prometheus::default_registry();
let app_metrics = BucketMetrics::builder().with_registry(registry).build();
let duration = Duration::from_secs(1);
app_metrics.hist().observe(duration.as_secs_f64());
let encoder = prometheus::TextEncoder::new();
let metric_families = registry.gather();
let mut buffer = vec![];
encoder.encode(&metric_families, &mut buffer).unwrap();
let output = String::from_utf8(buffer).unwrap();
assert!(output.contains("test_hist"));
}
#[test]
fn bucket_defaults_work() {
#[prometric_derive::metrics(scope = "test")]
struct BucketMetrics {
#[metric]
hist: prometric::Histogram,
}
let registry = prometheus::default_registry();
let app_metrics = BucketMetrics::builder().with_registry(registry).build();
let duration = Duration::from_secs(1);
app_metrics.hist().observe(duration.as_secs_f64());
let encoder = prometheus::TextEncoder::new();
let metric_families = registry.gather();
let mut buffer = vec![];
encoder.encode(&metric_families, &mut buffer).unwrap();
let output = String::from_utf8(buffer).unwrap();
assert!(output.contains("test_hist"));
}
#[test]
fn quantiles_defaults_work() {
#[prometric_derive::metrics(scope = "test")]
struct QuantileMetrics {
#[metric]
summary: prometric::Summary,
}
let registry = prometheus::default_registry();
let app_metrics = QuantileMetrics::builder().with_registry(registry).build();
let duration = Duration::from_secs(1);
app_metrics.summary().observe(duration.as_secs_f64());
let encoder = prometheus::TextEncoder::new();
let metric_families = registry.gather();
let mut buffer = vec![];
encoder.encode(&metric_families, &mut buffer).unwrap();
let output = String::from_utf8(buffer).unwrap();
assert!(output.contains("test_summary"));
}
#[test]
fn quantiles_with_batching_work() {
#[prometric_derive::metrics(scope = "test")]
struct QuantileMetrics {
#[metric]
summary: prometric::Summary,
}
let registry = prometheus::default_registry();
let app_metrics = QuantileMetrics::builder().with_registry(registry).build();
let duration = Duration::from_secs(1);
for i in 0..10000 {
let start = std::time::Instant::now();
println!("{i} ");
app_metrics.summary().observe(duration.as_secs_f64() * i as f64);
if i % 100 == 0 {
println!("Time taken: {:?}", start.elapsed());
}
}
let encoder = prometheus::TextEncoder::new();
let metric_families = registry.gather();
let mut buffer = vec![];
encoder.encode(&metric_families, &mut buffer).unwrap();
let output = String::from_utf8(buffer).unwrap();
println!("{output}");
assert!(output.contains("test_summary"));
}