Einleitung: Warum Logging für Rust-Anwendungen entscheidend ist
Logging ist nicht nur das Aufzeichnen von Ereignissen in einer Datei. In einer Produktionsumgebung ermöglichen hochwertige Logs eine schnelle Diagnose von Fehlern, die Überwachung der Leistung und die Analyse des Systemverhaltens. Rust, als systemnahe Programmiersprache, bietet flexible und leistungsstarke Werkzeuge für diese Aufgabe. In diesem Artikel werden wir die besten Praktiken für das Logging in Rust durchgehen: von der Auswahl der Crate bis zur Konfiguration von strukturierten Logs und Rotation.
1. Grundlagen: Die Crates log und env_logger
Das Rust-Ökosystem bietet einen modularen Ansatz. Die Crate log stellt eine Abstraktion (Fassade) bereit, während eine konkrete Implementierung (Backend) durch eine separate Crate wie env_logger oder pretty_env_logger eingebunden wird.
Installation und Konfiguration
Fügen Sie die Abhängigkeiten in Cargo.toml hinzu:
[dependencies]log = "0.4"env_logger = "0.10"Basisbeispiel
use log::{info, warn, error};
fn main() { env_logger::init();
info!("Anwendung gestartet"); let user_id = 42; warn!("Benutzer {} nicht im Cache gefunden", user_id); error!("Fehler bei der Datenbankverbindung: {:?}", "timeout");}Wichtig: Standardmäßig werden keine Logs ausgegeben, bis Sie die Umgebungsvariable RUST_LOG setzen. Starten Sie das Programm wie folgt:
RUST_LOG=info cargo runLog-Level
Die Crate log unterstützt fünf Standard-Level (von der höchsten zur niedrigsten Priorität):
error— Kritische Fehler, die sofortiges Eingreifen erfordern.warn— Warnungen vor potenziellen Problemen.info— Allgemeine Informationen zum Programmablauf.debug— Debug-Informationen für Entwickler.trace— Maximal detaillierte Ereignisse (häufig in Schleifen verwendet).
Filterung nach Modulen: RUST_LOG=my_crate=debug,other_crate=info.
2. Fortgeschrittenes Logging: Die Crate tracing
Für komplexe Anwendungen (Webserver, Microservices) wird die Verwendung der Crate tracing empfohlen. Sie bietet das Konzept von Spans (Zeitintervallen) und strukturiertes Logging mit Kontext.
Installation von tracing
[dependencies]tracing = "0.1"tracing-subscriber = "0.3"Beispiel mit Span
use tracing::{info, instrument, span, Level};use tracing_subscriber;
fn main() { tracing_subscriber::fmt::init();
let parent_span = span!(Level::INFO, "request_processing", request_id = 123); let _guard = parent_span.enter(); process_data();}
#[instrument]fn process_data() { info!("Datenverarbeitung gestartet"); std::thread::sleep(std::time::Duration::from_millis(50)); info!("Datenverarbeitung abgeschlossen");}Das Attribut #[instrument] erstellt automatisch einen Span für die Funktion und zeichnet die Ausführungszeit auf. Die Ausgabe enthält verschachtelte Zeitstempel, was für das Profiling unverzichtbar ist.
3. Strukturierte Logs (JSON) und Rotation
In einer Produktionsumgebung werden Logs oft zentralisiert gesammelt (ELK, Loki, Grafana). Dafür ist es praktisch, Logs im JSON-Format auszugeben.
JSON-Ausgabe mit tracing-subscriber
use tracing_subscriber::fmt::format::Json;use tracing_subscriber::prelude::*;
fn main() { let format = Json::new() .with_timer(tracing_subscriber::fmt::time::UtcTime::rfc3339()) .with_target(true) .with_level(true);
tracing_subscriber::fmt() .event_format(format) .init();
tracing::info!("Server gestartet auf Port 8080");}Die Logs sehen dann wie folgt aus: