Monitoreo de aplicaciones Rust: la biblioteca tracing y recopilación de métricas
El monitoreo es un elemento clave en los sistemas de producción. Para aplicaciones en Rust, conocidas por su rendimiento y confiabilidad, es importante tener una herramienta ligera y eficiente para recopilar telemetría. La biblioteca tracing se ha convertido en el estándar de facto para la observabilidad en el ecosistema Rust. Proporciona registro estructurado, spans para el rastreo de solicitudes e integración con sistemas de recopilación de métricas.
¿Qué es tracing?
tracing es un framework para la instrumentación de programas Rust. A diferencia del registro clásico, funciona basándose en eventos y spans. Los spans representan segmentos temporales de ejecución de código (por ejemplo, el procesamiento de una solicitud HTTP), mientras que los eventos son puntos dentro de los spans (por ejemplo, «la base de datos respondió en 10 ms»).
Características principales:
- Registro estructurado — cada evento contiene campos (clave-valor), no solo una cadena de texto.
- Jerarquía de spans — se pueden anidar spans unos dentro de otros, rastreando la ruta completa de ejecución.
- Soporte asíncrono — funciona excelentemente con tokio y async/await.
- Suscriptor (Subscriber) — se pueden conectar diferentes backends: stdout, OpenTelemetry, Prometheus, archivos.
Instalación y uso básico
Agregue a Cargo.toml:
[dependencies]tracing = "0.1"tracing-subscriber = "0.3"Ejemplo simple de registro estructurado:
use tracing::{info, warn, error};use tracing_subscriber;
fn main() { // Inicializamos el subscriber para salida en stdout tracing_subscriber::fmt::init();
let user_id = 42; let request_duration_ms = 150;
info!(user_id, duration = request_duration_ms, "Solicitud procesada");
// Registro con nivel warn warn!(user_id, "El tiempo de respuesta supera el umbral");
// Registro con error error!("No se pudo conectar a la BD");}La salida contendrá los campos en un formato similar a JSON, lo que facilita el análisis en sistemas de monitoreo.
Spans y rastreo
Los spans permiten medir el tiempo de ejecución de las operaciones y vincular eventos en una cadena:
use tracing::{span, info, Level};use tracing_subscriber;
fn process_request(user_id: u32) { // Creamos un span con contexto let span = span!(Level::INFO, "process_request", user_id); let _guard = span.enter(); // activamos el span
info!("Inicio del procesamiento"); std::thread::sleep(std::time::Duration::from_millis(50)); info!("Procesamiento completado");}
fn main() { tracing_subscriber::fmt::init(); process_request(100);}En la salida aparecerá un contexto anidado, mostrando que los eventos pertenecen al span process_request.
Recopilación de métricas con tracing y Prometheus
Para el monitoreo en producción, a menudo se requiere exportar métricas a Prometheus. Use el crate tracing-appender para escribir en un archivo o tracing-opentelemetry para integración con OpenTelemetry. Sin embargo, la forma más directa es usar la biblioteca metrics junto con tracing.
Ejemplo de integración con metrics-exporter-prometheus:
[dependencies]metrics = "0.21"metrics-exporter-prometheus = "0.12"tracing = "0.1"tokio = { version = "1", features = ["full"] }Código para recopilar métricas:
use metrics::{counter, gauge, histogram};use metrics_exporter_prometheus::PrometheusBuilder;use std::net::SocketAddr;
#[tokio::main]async fn main() { // Iniciamos el servidor HTTP para Prometheus let addr: SocketAddr = "0.0.0.0:9090".parse().unwrap(); let builder = PrometheusBuilder::new(); builder .wit