Observability in Rust: Tracing, Metriken und Logs für zuverlässige Systeme

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

Warum Observability eine Superkraft für Rust-Entwickler ist



Stellen Sie sich vor: Ihr Dienst in Rust läuft in der Produktion, aber plötzlich schießt die Latenz in die Höhe. Logs schweigen, Metriken zeigen 200 OK, und Benutzer beschweren sich. Kommt Ihnen bekannt vor? Das ist die klassische „Black Box“. Ohne Observability in Rust sind Sie blind.



Observability (Beobachtbarkeit) ist nicht nur „Logs + Metriken“. Es ist die Fähigkeit, dem System beliebige Fragen zu stellen, ohne neuen Code schreiben zu müssen. In Rust, mit seinem Fokus auf Leistung und Zuverlässigkeit, wird Observability zu einer zwingenden Zutat für Production-Grade-Anwendungen.



In diesem Artikel werden wir die drei Säulen der Observability in Rust behandeln: Tracing, Metriken und Logs. Sie erhalten fertige Codebeispiele mit tracing, OpenTelemetry und Prometheus, die Sie sofort in Ihr Projekt integrieren können.



Tracing in Rust: Die Reise einer Anfrage durch das System



Tracing ist die Lebensgeschichte einer einzelnen Anfrage. Statt isolierter Logs erhalten Sie einen zusammenhängenden Pfad: vom API-Eingang über die Datenbankabfrage bis zur Antwort an den Client. Für Rust ist das beste Werkzeug die Crate tracing aus der Tokio-Community.



Einrichtung von tracing in einer Rust-Anwendung



Beginnen wir mit der grundlegenden Einrichtung. Fügen Sie in Cargo.toml hinzu:



[dependencies]tracing = "0.1"tracing-subscriber = "0.3"tracing-opentelemetry = "0.23"opentelemetry = "0.22"opentelemetry-otlp = "0.15"


Jetzt initialisieren wir den Tracer in main.rs:



use tracing_subscriber::{fmt, prelude::*, EnvFilter};use opentelemetry::{global, KeyValue};use opentelemetry_otlp::WithExportConfig;

fn init_tracer() -> Result<(), Box<dyn std::error::Error>> { global::set_text_map_propagator(opentelemetry_jaeger::Propagator::new()); let tracer = opentelemetry_otlp::new_pipeline() .tracing() .with_exporter( opentelemetry_otlp::new_exporter() .tonic() .with_endpoint("http://localhost:4317") ) .with_trace_config( opentelemetry::sdk::trace::config() .with_resource(opentelemetry::sdk::Resource::new(vec![ KeyValue::new("service.name", "rust-observability-demo") ])) ) .install_simple()?;

// Verknüpfen von tracing mit OpenTelemetry let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); let subscriber = fmt::Subscriber::builder() .with_env_filter(EnvFilter::from_default_env()) .finish() .with(telemetry);

tracing::subscriber::set_global_default(subscriber)?; Ok(())}


Erstellen von Spans und Traces



Jetzt fügen wir Tracing in die Geschäftslogik ein:



use tracing::{span, Span, Level, instrument};

#[instrument]async fn handle_request(user_id: u64) -> Result<String, Box<dyn std::error::Error>> { let user_data = get_user_data(user_id).await?; let processed = process_data(&user_data).await?; Ok(processed)}

#[instrument(skip(db_pool))]async fn get_user_data(user_id: u64) -> Result<User, Box<dyn std::error::Error>> { // Simulation einer Datenbankabfrage let span = Span::current(); span.record("db.query", "SELECT * FROM users WHERE id = $1"); tokio::time::sleep(std::time::Duration::from_millis(50)).await; Ok(User { id: user_id, name: "Alice".into() })}

#[instrument]async fn process_data(user: &User) -> Result<String, Box<dyn std::error::Error>> { let result = format!("Processed user: {}", user.name); tracing::info!(user_id = user.id, "Data processed successfully"); Ok(result)}


Das Attribut #[instrument] erstellt automatisch einen Span für die Funktion und zeichnet alle Argumente auf. In Jaege

Blogs

Buchempfehlungen