[LOGGING] Fix #14: Support rotation logs Rust avec tracing-appender dans veza-common
This commit is contained in:
parent
112208df24
commit
4c9d28fb3c
2 changed files with 76 additions and 7 deletions
|
|
@ -37,6 +37,7 @@ sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres", "uuid"
|
|||
# Logging
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
tracing-appender = "0.2" # FIX #14: Support rotation des logs
|
||||
|
||||
# Configuration
|
||||
config = "0.13"
|
||||
|
|
|
|||
|
|
@ -1,16 +1,22 @@
|
|||
//! Logging utilities for Veza Rust services
|
||||
//!
|
||||
//! This module provides centralized logging configuration and utilities.
|
||||
//! FIX #14: Support rotation des logs avec tracing-appender
|
||||
|
||||
use tracing::{Level, Subscriber};
|
||||
use tracing_subscriber::{
|
||||
fmt::{self, format::FmtSpan},
|
||||
layer::SubscriberExt,
|
||||
util::SubscriberInitExt,
|
||||
EnvFilter, Registry,
|
||||
EnvFilter, Registry, Layer,
|
||||
};
|
||||
use tracing_appender::{
|
||||
non_blocking::WorkerGuard,
|
||||
rolling::{RollingFileAppender, Rotation},
|
||||
};
|
||||
|
||||
use crate::{VezaError, VezaResult};
|
||||
use crate::error::server::{VezaError, VezaResult};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// FIX #24: Normaliser le niveau de log depuis LOG_LEVEL ou RUST_LOG
|
||||
/// Convertit les niveaux Go (INFO, DEBUG, WARN, ERROR) vers les niveaux Rust (info, debug, warn, error)
|
||||
|
|
@ -65,6 +71,9 @@ pub fn init() -> VezaResult<()> {
|
|||
}
|
||||
|
||||
/// Initialize logging with custom configuration
|
||||
/// FIX #14: Support rotation des logs si config.file est défini
|
||||
/// Note: Le WorkerGuard doit être gardé en vie par l'appelant pour maintenir la rotation active
|
||||
/// Pour l'instant, on utilise une variable statique leakée (acceptable car le guard doit vivre toute la durée de vie de l'app)
|
||||
pub fn init_with_config(config: LoggingConfig) -> VezaResult<()> {
|
||||
// FIX #24: Normaliser le niveau de log depuis la config ou LOG_LEVEL
|
||||
let log_level = if !config.level.is_empty() && config.level != "info" {
|
||||
|
|
@ -88,6 +97,37 @@ pub fn init_with_config(config: LoggingConfig) -> VezaResult<()> {
|
|||
let env_filter = EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| EnvFilter::new(&log_level));
|
||||
|
||||
// FIX #14: Configurer la rotation des logs si un fichier est spécifié
|
||||
let (file_writer, guard) = if let Some(file_path) = &config.file {
|
||||
let path = PathBuf::from(file_path);
|
||||
let log_dir = path.parent()
|
||||
.ok_or_else(|| VezaError::Config("Invalid log file path".to_string()))?;
|
||||
let file_prefix = path.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.ok_or_else(|| VezaError::Config("Invalid log file name".to_string()))?;
|
||||
|
||||
// Déterminer la rotation selon max_size
|
||||
let rotation = if config.max_size > 100 * 1024 * 1024 {
|
||||
// > 100MB -> rotation quotidienne
|
||||
Rotation::DAILY
|
||||
} else {
|
||||
// <= 100MB -> rotation horaire
|
||||
Rotation::HOURLY
|
||||
};
|
||||
|
||||
let file_appender = RollingFileAppender::new(rotation, log_dir, file_prefix);
|
||||
let (non_blocking, worker_guard) = tracing_appender::non_blocking(file_appender);
|
||||
|
||||
// FIX #14: Le WorkerGuard doit être gardé en vie pour maintenir la rotation active
|
||||
// On utilise Box::leak pour le garder en vie pendant toute la durée de vie de l'application
|
||||
// C'est acceptable car le guard doit vivre tant que l'application tourne
|
||||
Box::leak(Box::new(worker_guard));
|
||||
|
||||
(Some(non_blocking), true)
|
||||
} else {
|
||||
(None, false)
|
||||
};
|
||||
|
||||
// FIX #25: Standardiser sur JSON en production/staging, texte en développement
|
||||
// Create formatting layer based on format
|
||||
let fmt_layer = match config.format.as_str() {
|
||||
|
|
@ -112,13 +152,41 @@ pub fn init_with_config(config: LoggingConfig) -> VezaResult<()> {
|
|||
_ => return Err(VezaError::Config(format!("Invalid log format: {}", config.format))),
|
||||
};
|
||||
|
||||
// Initialize the subscriber
|
||||
Registry::default()
|
||||
// FIX #14: Ajouter un layer pour les fichiers si rotation configurée
|
||||
let mut registry = Registry::default()
|
||||
.with(env_filter)
|
||||
.with(fmt_layer)
|
||||
.init();
|
||||
.with(fmt_layer);
|
||||
|
||||
if let Some(writer) = file_writer {
|
||||
// Layer pour les fichiers avec rotation (toujours en JSON pour faciliter l'agrégation)
|
||||
let file_layer = fmt::layer()
|
||||
.json()
|
||||
.with_writer(writer)
|
||||
.with_target(true)
|
||||
.with_thread_ids(true)
|
||||
.with_thread_names(true)
|
||||
.with_span_events(FmtSpan::CLOSE)
|
||||
.with_file(true)
|
||||
.with_line_number(true)
|
||||
.boxed();
|
||||
|
||||
registry = registry.with(file_layer);
|
||||
}
|
||||
|
||||
// Initialize the subscriber
|
||||
registry.init();
|
||||
|
||||
if guard {
|
||||
tracing::info!(
|
||||
file = ?config.file,
|
||||
max_size = config.max_size,
|
||||
max_files = config.max_files,
|
||||
"Logging initialized with file rotation"
|
||||
);
|
||||
} else {
|
||||
tracing::info!("Logging initialized with custom config");
|
||||
}
|
||||
|
||||
tracing::info!("Logging initialized with custom config");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue