Introducción: por qué el logging es crítico para aplicaciones Rust
El logging no es solo registrar eventos en un archivo. En un entorno de producción, los logs de calidad permiten diagnosticar errores rápidamente, monitorear el rendimiento y analizar el comportamiento del sistema. Rust, al ser un lenguaje de programación de sistemas, ofrece herramientas flexibles y de alto rendimiento para esta tarea. En este artículo, analizaremos las mejores prácticas de logging en Rust: desde la elección de la crate hasta la configuración de logs estructurados y rotación.
1. Fundamentos: las crates log y env_logger
El ecosistema de Rust ofrece un enfoque modular. La crate log proporciona una abstracción (fachada), y una crate separada, como env_logger o pretty_env_logger, conecta la implementación concreta (backend).
Instalación y configuración
Agregue las dependencias en Cargo.toml:
[dependencies]log = "0.4"env_logger = "0.10"Ejemplo básico
use log::{info, warn, error};
fn main() { env_logger::init();
info!("Aplicación iniciada"); let user_id = 42; warn!("Usuario {} no encontrado en caché", user_id); error!("Error de conexión a la base de datos: {:?}", "timeout");}Importante: Por defecto, los logs no se muestran hasta que establezca la variable de entorno RUST_LOG. Ejecute el programa así:
RUST_LOG=info cargo runNiveles de logging
La crate log admite cinco niveles estándar (del más prioritario al menos):
error— errores críticos que requieren intervención inmediata.warn— advertencias sobre problemas potenciales.info— información general sobre el progreso de la ejecución.debug— información de depuración para desarrolladores.trace— eventos con el máximo detalle (a menudo usados en bucles).
Filtrado por módulos: RUST_LOG=my_crate=debug,other_crate=info.
2. Logging avanzado: la crate tracing
Para aplicaciones complejas (servidores web, microservicios) se recomienda usar la crate tracing. Proporciona el concepto de span (intervalos de tiempo) y logging estructurado con contexto.
Instalación de tracing
[dependencies]tracing = "0.1"tracing-subscriber = "0.3"Ejemplo con span
use tracing::{info, instrument, span, Level};use tracing_subscriber;
fn main() { tracing_subscriber::fmt::init();
let parent_span = span!(Level::INFO, "procesamiento_solicitud", request_id = 123); let _guard = parent_span.enter(); process_data();}
#[instrument]fn process_data() { info!("Procesamiento de datos iniciado"); std::thread::sleep(std::time::Duration::from_millis(50)); info!("Procesamiento de datos completado");}El atributo #[instrument] crea automáticamente un span para la función y registra el tiempo de ejecución. La salida contendrá marcas de tiempo anidadas, lo cual es indispensable para la creación de perfiles.
3. Logs estructurados (JSON) y rotación
En un entorno de producción, los logs a menudo se recopilan de forma centralizada (ELK, Loki, Grafana). Para ello, es conveniente generar logs en formato JSON.
Salida en JSON con 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!("Servidor iniciado en el puerto 8080");}Ahora los logs se verán así: