Introduction : pourquoi la journalisation est cruciale pour les applications Rust
La journalisation ne consiste pas seulement à enregistrer des événements dans un fichier. Dans un environnement de production, des logs de qualité permettent de diagnostiquer rapidement les erreurs, de suivre les performances et d'analyser le comportement du système. Rust, en tant que langage de programmation système, offre des outils flexibles et performants pour cette tâche. Dans cet article, nous allons analyser les meilleures pratiques de journalisation en Rust : du choix de la crate à la configuration des logs structurés et de la rotation.
1. Les bases : les crates log et env_logger
L'écosystème Rust propose une approche modulaire. La crate log fournit une abstraction (façade), et une implémentation concrète (backend) est connectée par une crate séparée, par exemple env_logger ou pretty_env_logger.
Installation et configuration
Ajoutez les dépendances dans Cargo.toml :
[dependencies]log = "0.4"env_logger = "0.10"Exemple de base
use log::{info, warn, error};
fn main() { env_logger::init();
info!("Application démarrée"); let user_id = 42; warn!("Utilisateur {} non trouvé dans le cache", user_id); error!("Erreur de connexion à la base de données : {:?}", "timeout");}Important : Par défaut, les logs ne s'affichent pas tant que vous n'avez pas défini la variable d'environnement RUST_LOG. Lancez le programme ainsi :
RUST_LOG=info cargo runNiveaux de journalisation
La crate log prend en charge cinq niveaux standard (du plus prioritaire au moins prioritaire) :
error— erreurs critiques nécessitant une intervention immédiate.warn— avertissements concernant des problèmes potentiels.info— informations générales sur le déroulement de l'exécution.debug— informations de débogage pour les développeurs.trace— événements extrêmement détaillés (souvent utilisés dans les boucles).
Filtrage par modules : RUST_LOG=my_crate=debug,other_crate=info.
2. Journalisation avancée : la crate tracing
Pour les applications complexes (serveurs web, microservices), il est recommandé d'utiliser la crate tracing. Elle introduit le concept de span (intervalles de temps) et de journalisation structurée avec contexte.
Installation de tracing
[dependencies]tracing = "0.1"tracing-subscriber = "0.3"Exemple avec span
use tracing::{info, instrument, span, Level};use tracing_subscriber;
fn main() { tracing_subscriber::fmt::init();
let parent_span = span!(Level::INFO, "traitement_requete", request_id = 123); let _guard = parent_span.enter(); process_data();}
#[instrument]fn process_data() { info!("Traitement des données commencé"); std::thread::sleep(std::time::Duration::from_millis(50)); info!("Traitement des données terminé");}L'attribut #[instrument] crée automatiquement un span pour la fonction et enregistre le temps d'exécution. La sortie contiendra des horodatages imbriqués, ce qui est indispensable pour le profilage.
3. Logs structurés (JSON) et rotation
Dans un environnement de production, les logs sont souvent collectés de manière centralisée (ELK, Loki, Grafana). Pour cela, il est pratique de sortir les logs au format JSON.
Sortie en JSON avec 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!("Serveur démarré sur le port 8080");}Désormais, les logs ressembleront à ceci :