[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
|
# Logging
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
|
tracing-appender = "0.2" # FIX #14: Support rotation des logs
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
config = "0.13"
|
config = "0.13"
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,22 @@
|
||||||
//! Logging utilities for Veza Rust services
|
//! Logging utilities for Veza Rust services
|
||||||
//!
|
//!
|
||||||
//! This module provides centralized logging configuration and utilities.
|
//! This module provides centralized logging configuration and utilities.
|
||||||
|
//! FIX #14: Support rotation des logs avec tracing-appender
|
||||||
|
|
||||||
use tracing::{Level, Subscriber};
|
use tracing::{Level, Subscriber};
|
||||||
use tracing_subscriber::{
|
use tracing_subscriber::{
|
||||||
fmt::{self, format::FmtSpan},
|
fmt::{self, format::FmtSpan},
|
||||||
layer::SubscriberExt,
|
layer::SubscriberExt,
|
||||||
util::SubscriberInitExt,
|
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
|
/// 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)
|
/// 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
|
/// 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<()> {
|
pub fn init_with_config(config: LoggingConfig) -> VezaResult<()> {
|
||||||
// FIX #24: Normaliser le niveau de log depuis la config ou LOG_LEVEL
|
// FIX #24: Normaliser le niveau de log depuis la config ou LOG_LEVEL
|
||||||
let log_level = if !config.level.is_empty() && config.level != "info" {
|
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()
|
let env_filter = EnvFilter::try_from_default_env()
|
||||||
.unwrap_or_else(|_| EnvFilter::new(&log_level));
|
.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
|
// FIX #25: Standardiser sur JSON en production/staging, texte en développement
|
||||||
// Create formatting layer based on format
|
// Create formatting layer based on format
|
||||||
let fmt_layer = match config.format.as_str() {
|
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))),
|
_ => return Err(VezaError::Config(format!("Invalid log format: {}", config.format))),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize the subscriber
|
// FIX #14: Ajouter un layer pour les fichiers si rotation configurée
|
||||||
Registry::default()
|
let mut registry = Registry::default()
|
||||||
.with(env_filter)
|
.with(env_filter)
|
||||||
.with(fmt_layer)
|
.with(fmt_layer);
|
||||||
.init();
|
|
||||||
|
|
||||||
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue