Logging in Rust-Anwendungen: Best Practices in der Praxis

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Logging in Rust-Anwendungen: Best Practices in der Praxis



Logging ist eines der Schlüsselelemente jeder Production-Anwendung. Es hilft, Fehler zu diagnostizieren, das Systemverhalten zu verfolgen und Metriken zu sammeln. Im Rust-Ökosystem gibt es dafür mehrere ausgereifte Lösungen. In diesem Artikel werden wir die Best Practices für das Logging anhand realer Codebeispiele erläutern.



1. Grundlagen: crate log und Fassadenarchitektur



In Rust ist das crate log der De-facto-Standard. Es bietet eine abstrakte Fassade, die nicht an eine bestimmte Implementierung gebunden ist. Das bedeutet, Sie schreiben Logs über die Makros von log, und welches Backend diese verarbeitet (Ausgabe in die Konsole, in eine Datei, Senden an ELK) – wird beim Einbinden eines anderen Crates entschieden.



Einrichtung des grundlegenden Loggings



// Cargo.toml[dependencies]log = "0.4"env_logger = "0.11"


// main.rsuse log::{info, warn, error, debug};

fn main() { // env_logger initialisieren (liest die Variable RUST_LOG) env_logger::init();

info!("Anwendung gestartet"); warn!("Standardkonfiguration wird verwendet"); error!("Kritischer Fehler: {}", "Datei nicht gefunden");}


Starten Sie die Anwendung mit der Umgebungsvariable:

RUST_LOG=info cargo run


Sie sehen auf dem Bildschirm:

[2025-01-15T10:30:00Z INFO  my_app] Anwendung gestartet[2025-01-15T10:30:00Z WARN  my_app] Standardkonfiguration wird verwendet[2025-01-15T10:30:00Z ERROR my_app] Kritischer Fehler: Datei nicht gefunden


2. Flexible Konfiguration der Log-Level



Die Variable RUST_LOG ermöglicht eine flexible Filterung von Nachrichten nach Modulen und Leveln. Dies ist für große Projekte entscheidend, da Rauschen durch Debug-Logs wichtige Ereignisse überdecken kann.



Beispiele für Filterung



# Nur Fehler und Warnungen aus der gesamten AnwendungRUST_LOG=warn

# Debug nur im Modul database, der Rest infoRUST_LOG=info,my_crate::database=debug

# Alle Level, einschließlich traceRUST_LOG=trace


In der Praxis verwenden Sie diesen Ansatz:

use std::env;

fn setup_logging() { if env::var("RUST_LOG").is_err() { // Standardwert für die Entwicklung env::set_var("RUST_LOG", "info,my_crate=debug"); } env_logger::init();}


3. Strukturiertes Logging mit tracing



Für komplexe Systeme (Mikroservices, asynchrone Anwendungen) ist das crate tracing besser geeignet. Es unterstützt Spans und strukturierte Felder, wodurch Logs in Anforderungsketten verknüpft werden können.



Vergleich von log und tracing



  • log – einfach, leichtgewichtig, geeignet für kleine Projekte.
  • tracing – leistungsstark, unterstützt Spans, Ereignisse, strukturierte Felder; ideal für Async-Code und Systeme mit Tracing-Instrumentierung.


Beispiel mit tracing und tracing-subscriber



// Cargo.toml[dependencies]tracing = "0.1"tracing-subscriber = "0.3"


use tracing::{info, error, span, Level};use tracing_subscriber::FmtSubscriber;

#[tokio::main]async fn main() { // Subscriber für die Ausgabe in stdout konfigurieren let subscriber = FmtSubscriber::builder() .with_max_level(Level::TRACE) .finish(); tracing::subscriber::set_global_default(subscriber) .expect("Subscriber konnte nicht gesetzt werden");

// Einen Span für eine HTTP-Anfrage erstellen let request_span = span!(Level::INFO, "http_request", method = "GET", path = "/api/users"); let _guard = request_span.enter();

info!("Anfrageverarbeitung gestartet");

// Verschachtelter Span für die Datenbankarbeit let db_span = span!(Level::DEBUG, "db_query", query = "SELECT * FROM users"); let _db_gu

Blogs

Buchempfehlungen