Überwachung von Rust-Anwendungen: Metriken und Alarme von A bis Z
\n\nWenn Ihre Rust-Anwendung in die Produktion geht, ist der Code nicht mehr nur Code. Er wird zu einem Dienst, von dem Benutzer und Unternehmen abhängen. Ohne Überwachung fliegen Sie blind: Ausfälle werden durch Beschwerden entdeckt, und die Leistung wird „nach Augenmaß“ bewertet. In diesem Artikel werden wir uns ansehen, wie Sie ein vollständiges Überwachungssystem für Rust aufbauen – von der Erfassung grundlegender Metriken bis zur Konfiguration von Alarmen in Prometheus und der Visualisierung in Grafana.
\n\n1. Warum Überwachung für Rust-Projekte wichtig ist
\nRust ist bekannt für seine Leistung und Speichersicherheit, aber das macht es nicht überflüssig, die Anwendung im Auge zu behalten. Typische Probleme in der Produktion:
\n- \n
- Speicherlecks (z. B. durch
Arcoder zyklische Referenzen). \n - Blockierungen in async-Code (Deadlocks oder Starvation in tokio). \n
- Unerwartete Paniken (selbst mit
catch_unwind). \n - Leistungsverschlechterung durch suboptimale Algorithmen. \n
Überwachung liefert Antworten auf die Fragen: „Was passiert gerade?“, „Warum ist das passiert?“ und „Wie kann man es verhindern?“.
\n\n2. Metrikerfassung: Die Bibliothek metrics und Export nach Prometheus
\nDer Standard-Stack für Rust ist die Crate metrics (oder prometheus). Wir verwenden metrics, da es den Export abstrahiert und das Backend leicht gewechselt werden kann.
2.1. Installation und grundlegende Konfiguration
\nFügen Sie die Abhängigkeiten in Cargo.toml hinzu:
[dependencies]\nmetrics = "0.21"\nmetrics-exporter-prometheus = "0.12"\ntokio = { version = "1", features = ["full"] }\n\nWir erstellen einen einfachen HTTP-Server für den Endpunkt /metrics:
use metrics_exporter_prometheus::PrometheusBuilder;\nuse std::net::SocketAddr;\nuse tokio::net::TcpListener;\n\n#[tokio::main]\nasync fn main() {\n // Initialisieren des Prometheus-Exporters\n let (recorder, exporter) = PrometheusBuilder::new()\n .build()\n .expect("Konnte den Prometheus-Exporter nicht erstellen");\n\n metrics::set_boxed_recorder(Box::new(recorder))\n .expect("Konnte den globalen Recorder nicht setzen");\n\n // Starten des Metrik-Servers\n let addr: SocketAddr = "0.0.0.0:9090".parse().unwrap();\n let listener = TcpListener::bind(addr).await.unwrap();\n println!("Metriken verfügbar unter http://{}", addr);\n\n loop {\n let (mut stream, _) = listener.accept().await.unwrap();\n let body = exporter.encode().unwrap();\n let response = format!(\n "HTTP/1.1 200 OK\\r\\nContent-Type: text/plain\\r\\nContent-Length: {}\\r\\n\\r\\n{}",\n body.len(),\n body\n );\n tokio::io::AsyncWriteExt::write_all(&mut stream, response.as_bytes())\n .await\n .unwrap();\n }\n}\n\n2.2. Registrierung benutzerdefinierter Metriken
\nFügen wir einen Anfragezähler und ein Histogramm der Antwortzeit hinzu:
\nuse metrics::{counter, histogram, increment_counter};\nuse std::time::Instant;\n\nasync fn handle_request() {\n let start = Instant::now();\n\n // Ihre Geschäftslogik\n tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;\n\n // Metriken aufzeichnen\n increment_counter!("http_requests_total", "method" => "GET");\n histogram!("http_request_duration_seconds", start.elapsed().as_secs_f64(), "method" => "GET");\n}\nJetzt sehen Sie unter /metrics:
# HELP http_requests_total Total HTTP requests\n# TYPE http_requests_total counter\nhttp_requests_total{method="GET"} 1\n# HELP http_request_duration_seconds HTTP request duration\n# TYPE http_request_duration_seconds histogram\nhttp_request_duration_seconds_bucket{method="GET",le="0.005"} 0\nhttp_request_duration_seconds_bucket{method="GET",le="0.01"} 0\nhttp_request_duration_seconds_bucket{method="GET",le="0.025"} 0\n