Prometheus Metrics in Rust: Performance Monitoring
Prometheus is the de facto standard for collecting and analyzing metrics in modern microservice architectures. In the Rust ecosystem, there are several mature libraries that make it easy to integrate metric export in Prometheus format. In this article, we will look at two main libraries: prometheus (official client) and metrics-exporter-prometheus (part of the metrics framework).
Why are metrics needed in Rust?
Monitoring Rust applications is critical for:
- Tracking performance (latency, throughput)
- Identifying memory leaks and resource issues
- Setting up alerts based on threshold values
- Analyzing trends and capacity planning
The prometheus Library (prometheus-rs)
The official Rust client for Prometheus. Provides a full set of tools for creating and exporting metrics.
Installation
Add to Cargo.toml:
[dependencies]prometheus = { version = "0.13", features = ["process"] }hyper = { version = "0.14", features = ["http1", "server"] }Basic Example: HTTP Server with Metrics
use prometheus::{Counter, HistogramOpts, HistogramVec, IntCounter, Registry, TextEncoder};use std::net::SocketAddr;use hyper::{Body, Request, Response, Server};use hyper::service::{make_service_fn, service_fn};use std::convert::Infallible;use std::sync::Arc;use tokio::sync::Mutex;
#[tokio::main]async fn main() { let registry = Registry::new();
// Create a request counter let request_counter = IntCounter::new("http_requests_total", "Total HTTP requests") .unwrap(); registry.register(Box::new(request_counter.clone())).unwrap();
// Histogram for response time let request_duration = HistogramVec::new( HistogramOpts::new("http_request_duration_seconds", "HTTP request duration"), &["method", "path"] ).unwrap(); registry.register(Box::new(request_duration.clone())).unwrap();
let data = Arc::new(Mutex::new((request_counter, request_duration)));
let make_svc = make_service_fn(move |_conn| { let data = data.clone(); async move { Ok::<_, Infallible>(service_fn(move |req: Request| { let data = data.clone(); async move { let start = std::time::Instant::now(); let (counter, duration) = &*data.lock().await; counter.inc();
// Handle request let response = match req.uri().path() { "/metrics" => { let encoder = TextEncoder::new(); let metric_families = registry.gather(); let mut buffer = vec![]; encoder.encode(&metric_families, &mut buffer).unwrap(); Response::new(Body::from(buffer)) } "/" => Response::new(Body::from("Hello, World!")), _ => Response::builder() .status(404) .body(Body::from("Not Found")) .unwrap(), };
let elapsed = start.elapsed().as_secs_f64(); duration .with_label_values(&[req.method().as_str(), req.uri().path()]) .observe(elapsed);
Ok::<_, Infallible>(response) } })) } });
let addr = SocketAddr::from(([127, 0, 0, 1], 8080)); let server = Server::bind(&addr).serve(make_svc); println!("Listening on http://{}", addr); if let Err(e) = server.a