Observabilidad en Rust: trazado, métricas y registros para sistemas confiables

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Por qué la observabilidad es una superpotencia para el desarrollador de Rust



Imagine: su servicio en Rust funciona en producción, pero de repente la latencia se dispara. Los registros están en silencio, las métricas muestran 200 OK y los usuarios se quejan. ¿Le suena familiar? Esto es la clásica «caja negra». Sin observabilidad en Rust, está ciego.



Observabilidad no es solo «registros + métricas». Es la capacidad de hacer cualquier pregunta al sistema sin necesidad de escribir nuevo código. En Rust, con su énfasis en rendimiento y confiabilidad, la observabilidad se convierte en un ingrediente obligatorio para aplicaciones de grado de producción.



En este artículo, analizaremos los tres pilares de la observabilidad en Rust: trazado, métricas y registros. Obtendrá ejemplos de código listos para usar con tracing, OpenTelemetry y Prometheus que puede implementar de inmediato en su proyecto.



Trazado en Rust: el viaje de una solicitud a través del sistema



Trazado (tracing) es la historia de vida de una sola solicitud. En lugar de registros dispersos, obtiene una ruta vinculada: desde la entrada a la API hasta la consulta a la base de datos y la respuesta al cliente. Para Rust, la mejor herramienta es el crate tracing de la comunidad Tokio.



Configuración de tracing en una aplicación Rust



Comencemos con la configuración básica. Agregue a Cargo.toml:



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


Ahora inicialicemos el trazador en 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()?;

// Vinculamos tracing con 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(())}


Creación de spans y traces



Ahora agreguemos trazado a la lógica de negocio:



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>> { // Simulación de consulta a BD 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)}


El atributo #[instrument] crea automáticamente un span para la función y registra todos los argumentos. En Jaege

Blogs

Book Recommendations