Prometheus-Metriken in Rust: Leistungsüberwachung
Prometheus ist der De-facto-Standard für die Erfassung und Analyse von Metriken in modernen Microservice-Architekturen. Im Rust-Ökosystem gibt es mehrere ausgereifte Bibliotheken, die eine einfache Integration des Metrik-Exports im Prometheus-Format ermöglichen. In diesem Artikel betrachten wir zwei Hauptbibliotheken: prometheus (offizieller Client) und metrics-exporter-prometheus (Teil des metrics-Frameworks).
Warum sind Metriken in Rust wichtig?
Das Monitoring von Rust-Anwendungen ist kritisch für:
- Verfolgung der Leistung (Latenz, Durchsatz)
- Erkennung von Speicherlecks und Ressourcenproblemen
- Einrichtung von Alarmen basierend auf Schwellenwerten
- Analyse von Trends und Kapazitätsplanung
Die Bibliothek prometheus (prometheus-rs)
Offizieller Rust-Client für Prometheus. Bietet einen vollständigen Satz von Werkzeugen zum Erstellen und Exportieren von Metriken.
Installation
Fügen Sie in Cargo.toml hinzu:
[dependencies]prometheus = { version = "0.13", features = ["process"] }hyper = { version = "0.14", features = ["http1", "server"] }Basisbeispiel: HTTP-Server mit Metriken
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();
// Erstellen eines Anfragenzählers let request_counter = IntCounter::new("http_requests_total", "Total HTTP requests") .unwrap(); registry.register(Box::new(request_counter.clone())).unwrap();
// Histogramm für Antwortzeit 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();
// Anfrage verarbeiten 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