# đŸ—ïž PLAN DE REFONTE DU SYSTÈME DE LOGS VEZA **Date**: 2025-01-27 **Version**: 1.0 **Statut**: 📋 Plan d'Action --- ## 📋 TABLE DES MATIÈRES 1. [Architecture Cible](#1-architecture-cible) 2. [Plan d'ImplĂ©mentation](#2-plan-dimplĂ©mentation) 3. [Standards de Logging](#3-standards-de-logging) 4. [Configuration UnifiĂ©e](#4-configuration-unifiĂ©e) 5. [CorrĂ©lation DistribuĂ©e](#5-corrĂ©lation-distribuĂ©e) 6. [Checklist d'ImplĂ©mentation](#6-checklist-dimplĂ©mentation) --- ## 1. ARCHITECTURE CIBLE ### 1.1 Vue d'Ensemble ``` ┌─────────────────────────────────────────────────────────────────┐ │ SYSTÈME DE LOGS UNIFIÉ │ └─────────────────────────────────────────────────────────────────┘ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Backend Go │ │ Chat Server │ │Stream Server │ │ (zap) │ │ (tracing) │ │ (tracing) │ └──────┬────────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ Request ID │ Request ID │ Request ID │ Trace ID │ Trace ID │ Trace ID │ │ │ └──────────────────┮─────────────────┘ │ â–Œ ┌──────────────────────┐ │ Log Aggregator │ │ (Loki) │ └──────────┬───────────┘ │ â–Œ ┌──────────────────────┐ │ Grafana Dashboards │ │ (Visualisation) │ └──────────────────────┘ ``` ### 1.2 Composants #### 1.2.1 Backend Go (veza-backend-api) - **Logger** : `zap` (structurĂ©, performant) - **Format** : JSON en production, console en dĂ©veloppement - **Niveaux** : DEBUG, INFO, WARN, ERROR, FATAL - **CorrĂ©lation** : Request ID, Trace ID, Span ID - **AgrĂ©gation** : Loki (activĂ© par dĂ©faut en prod) #### 1.2.2 Services Rust (chat-server, stream-server) - **Logger** : `tracing` (framework moderne) - **Format** : JSON en production, texte en dĂ©veloppement - **Niveaux** : trace, debug, info, warn, error - **CorrĂ©lation** : Request ID, Trace ID via spans - **AgrĂ©gation** : Loki (via HTTP ou gRPC) #### 1.2.3 Frontend React (apps/web) - **Logger** : Logger structurĂ© custom (JSON) - **Format** : JSON (toujours) - **Niveaux** : debug, info, warn, error - **CorrĂ©lation** : Request ID (extrait des rĂ©ponses API) - **AgrĂ©gation** : Endpoint backend `/api/v1/logs` (optionnel) ### 1.3 Flux de Logs ``` 1. RequĂȘte HTTP arrive au Backend Go └─> GĂ©nĂšre Request ID (UUID v4) └─> GĂ©nĂšre Trace ID (si OpenTelemetry activĂ©) └─> Log de dĂ©but de requĂȘte 2. Backend Go appelle Chat Server └─> Propage Request ID via header X-Request-ID └─> Chat Server extrait Request ID et l'utilise dans les spans 3. Backend Go appelle Stream Server └─> Propage Request ID via header X-Request-ID └─> Stream Server extrait Request ID et l'utilise dans les spans 4. Frontend fait une requĂȘte API └─> Backend rĂ©pond avec X-Request-ID └─> Frontend extrait Request ID et l'inclut dans tous les logs 5. Tous les logs sont envoyĂ©s vers Loki └─> Labels: service, env, request_id, trace_id └─> Recherche possible par Request ID pour tracer une requĂȘte complĂšte ``` --- ## 2. PLAN D'IMPLÉMENTATION ### Phase 1 : Corrections Critiques (Semaine 1-2) #### 1.1 Corriger la Configuration du Logger Go **Fichier** : `veza-backend-api/internal/config/config.go` **Actions** : - [ ] Lire `LOG_LEVEL` AVANT d'initialiser le logger - [ ] Supprimer l'initialisation dupliquĂ©e dans `main.go` - [ ] Utiliser `NewLoggerWithAggregation` directement si agrĂ©gation activĂ©e - [ ] Garantir le flush au shutdown via `ShutdownManager` **Code cible** : ```go // Lire LOG_LEVEL en premier logLevel := getEnv("LOG_LEVEL", "INFO") // Initialiser le logger directement avec la bonne config var logger *zap.Logger if config.LogAggregationEnabled && config.LogAggregationEndpoint != "" { aggLogger, err := logging.NewLoggerWithAggregation(env, logLevel, aggConfig) if err != nil { // Fallback vers logger standard logger = createStandardLogger(env, logLevel) } else { logger = aggLogger.GetZapLogger() } } else { logger = createStandardLogger(env, logLevel) } ``` #### 1.2 ImplĂ©menter le Filtre de Secrets **Fichier** : `veza-backend-api/internal/logging/secret_filter.go` (nouveau) **Actions** : - [ ] CrĂ©er un `zapcore.Core` qui filtre les secrets - [ ] Liste de patterns Ă  filtrer : `password`, `secret`, `token`, `key`, `credential` - [ ] Remplacer les valeurs par `[REDACTED]` - [ ] IntĂ©grer dans la chaĂźne de cores zap **Code cible** : ```go type SecretFilterCore struct { core zapcore.Core } func (f *SecretFilterCore) Write(entry zapcore.Entry, fields []zapcore.Field) error { // Filtrer les champs sensibles filteredFields := make([]zapcore.Field, 0, len(fields)) for _, field := range fields { if isSecretField(field.Key) { filteredFields = append(filteredFields, zap.String(field.Key, "[REDACTED]")) } else { filteredFields = append(filteredFields, field) } } return f.core.Write(entry, filteredFields) } ``` #### 1.3 Remplacer fmt.Println par Logs StructurĂ©s **Fichiers** : 12 fichiers identifiĂ©s **Actions** : - [ ] Remplacer tous les `fmt.Print*` par `logger.Debug/Info/Warn/Error` - [ ] Supprimer les logs de debug excessifs (`=== DEBUG ===`) - [ ] Utiliser `logger.Debug()` pour les logs de debug (dĂ©sactivĂ©s en prod) **Exemple** : ```go // AVANT fmt.Printf(">>> ERROR STRING: %s\n", err.Error()) // APRÈS logger.Error("Registration failed", zap.Error(err), zap.String("request_id", requestID), ) ``` #### 1.4 Propager Request ID vers Services Rust **Fichiers** : - `veza-backend-api/internal/services/chat_service.go` - `veza-backend-api/internal/services/stream_service.go` - `veza-chat-server/src/main.rs` - `veza-stream-server/src/main.rs` **Actions** : - [ ] Extraire `request_id` du contexte Gin - [ ] Ajouter header `X-Request-ID` dans les appels HTTP vers Rust - [ ] Extraire `X-Request-ID` dans les services Rust - [ ] Utiliser `request_id` dans les spans tracing **Code Go** : ```go // Dans chat_service.go requestID := c.GetString("request_id") req.Header.Set("X-Request-ID", requestID) ``` **Code Rust** : ```rust // Dans main.rs ou middleware let request_id = headers.get("x-request-id") .and_then(|h| h.to_str().ok()) .map(|s| s.to_string()); if let Some(rid) = request_id { let span = tracing::span!(tracing::Level::INFO, "request", request_id = %rid); let _guard = span.enter(); // ... traitement } ``` ### Phase 2 : Standardisation (Semaine 3-4) #### 2.1 Unifier la Configuration Rust **Fichiers** : - `veza-common/src/logging.rs` (amĂ©liorer) - `veza-chat-server/src/main.rs` - `veza-stream-server/src/main.rs` **Actions** : - [ ] Refactoriser pour utiliser `veza-common::logging` partout - [ ] Supprimer la configuration dupliquĂ©e - [ ] Ajouter support de la rotation de logs - [ ] Standardiser sur `LOG_LEVEL` (au lieu de `RUST_LOG`) **Code cible** : ```rust // Dans main.rs des services Rust use veza_common::logging; fn main() -> Result<()> { // Lire LOG_LEVEL (standardisĂ©) let log_level = std::env::var("LOG_LEVEL") .unwrap_or_else(|_| "info".to_string()); let config = logging::LoggingConfig { level: log_level, format: if is_prod { "json" } else { "text" }.to_string(), file: Some("/var/log/veza/chat.log".to_string()), max_size: 100 * 1024 * 1024, // 100MB max_files: 5, compress: true, }; logging::init_with_config(config)?; // ... } ``` #### 2.2 Logger StructurĂ© Frontend **Fichier** : `apps/web/src/utils/logger.ts` (refactoriser) **Actions** : - [ ] CrĂ©er un logger structurĂ© avec format JSON - [ ] Extraire `X-Request-ID` des rĂ©ponses API - [ ] Inclure `request_id` dans tous les logs - [ ] Option d'envoyer les logs vers un endpoint backend - [ ] Remplacer tous les `console.log` par le nouveau logger **Code cible** : ```typescript interface LogEntry { level: 'debug' | 'info' | 'warn' | 'error'; message: string; timestamp: string; request_id?: string; user_id?: string; context?: Record; } class StructuredLogger { private requestId?: string; setRequestId(id: string) { this.requestId = id; } private log(level: LogEntry['level'], message: string, context?: Record) { const entry: LogEntry = { level, message, timestamp: new Date().toISOString(), request_id: this.requestId, context, }; if (import.meta.env.PROD) { // En production, envoyer vers endpoint backend this.sendToBackend(entry); } else { // En dĂ©veloppement, afficher dans la console console[level](JSON.stringify(entry, null, 2)); } } info(message: string, context?: Record) { this.log('info', message, context); } // ... autres mĂ©thodes } ``` #### 2.3 Activer l'AgrĂ©gation par DĂ©faut **Fichier** : `veza-backend-api/internal/config/config.go` **Actions** : - [ ] Activer `LOG_AGGREGATION_ENABLED=true` par dĂ©faut en production - [ ] Configurer l'endpoint Loki par dĂ©faut - [ ] Documenter la configuration **Configuration** : ```env # En production, activer par dĂ©faut LOG_AGGREGATION_ENABLED=true LOG_AGGREGATION_ENDPOINT=http://loki:3100/loki/api/v1/push LOG_AGGREGATION_BATCH_SIZE=100 LOG_AGGREGATION_FLUSH_INTERVAL=5s ``` ### Phase 3 : AmĂ©liorations (Semaine 5-6) #### 3.1 ImplĂ©menter le Sampling **Fichier** : `veza-backend-api/internal/logging/sampler.go` (nouveau) **Actions** : - [ ] CrĂ©er un `zapcore.Sampler` pour les logs DEBUG/INFO - [ ] Configurer le taux de sampling (ex: 10% pour DEBUG, 50% pour INFO) - [ ] Toujours logger les WARN/ERROR (100%) **Code cible** : ```go func NewSamplingCore(core zapcore.Core, config SamplingConfig) zapcore.Core { return zapcore.NewSamplerWithOptions( core, time.Second, config.DebugSampleRate, // Ex: 10 config.InfoSampleRate, // Ex: 50 ) } ``` #### 3.2 Ajouter Error Tracking Frontend **Fichier** : `apps/web/src/utils/errorTracking.ts` (nouveau) **Actions** : - [ ] IntĂ©grer Sentry pour le frontend - [ ] Capturer les erreurs non gĂ©rĂ©es - [ ] Capturer les erreurs React (ErrorBoundary) - [ ] Inclure le `request_id` dans les contextes Sentry #### 3.3 ImplĂ©menter la Rotation des Logs (Rust) **Fichier** : `veza-common/src/logging.rs` **Actions** : - [ ] Utiliser `tracing-appender` pour la rotation - [ ] Configurer la taille max et le nombre de fichiers - [ ] Compression des anciens logs **Code cible** : ```rust use tracing_appender::{rolling, non_blocking}; let file_appender = rolling::daily("/var/log/veza", "chat.log"); let (non_blocking_appender, _guard) = non_blocking(file_appender); let file_layer = fmt::layer() .with_writer(non_blocking_appender) .json() .boxed(); ``` #### 3.4 Logging Asynchrone **Fichier** : `veza-backend-api/internal/logging/async_logger.go` (nouveau) **Actions** : - [ ] CrĂ©er un logger asynchrone pour les logs non critiques - [ ] Utiliser un buffer avec goroutine - [ ] Flush pĂ©riodique et au shutdown --- ## 3. STANDARDS DE LOGGING ### 3.1 Format StandardisĂ© #### Backend Go (zap) ```json { "timestamp": "2025-01-27T10:30:45.123Z", "level": "info", "message": "Request completed", "service": "veza-api", "env": "production", "request_id": "550e8400-e29b-41d4-a716-446655440000", "trace_id": "1234567890abcdef", "span_id": "abcdef1234567890", "method": "POST", "path": "/api/v1/tracks", "status": 200, "latency_ms": 45, "user_id": "user-123", "ip": "192.168.1.1" } ``` #### Services Rust (tracing) ```json { "timestamp": "2025-01-27T10:30:45.123Z", "level": "info", "message": "Message sent", "service": "veza-chat", "env": "production", "request_id": "550e8400-e29b-41d4-a716-446655440000", "trace_id": "1234567890abcdef", "span_id": "abcdef1234567890", "conversation_id": "conv-123", "user_id": "user-456" } ``` #### Frontend (JSON) ```json { "timestamp": "2025-01-27T10:30:45.123Z", "level": "info", "message": "Track uploaded successfully", "service": "veza-web", "env": "production", "request_id": "550e8400-e29b-41d4-a716-446655440000", "user_id": "user-123", "track_id": "track-456", "url": "/tracks/456" } ``` ### 3.2 Niveaux de Log | Niveau | Usage | Exemples | |--------|-------|----------| | **DEBUG** | Informations dĂ©taillĂ©es pour le debugging | Variables, Ă©tats intermĂ©diaires | | **INFO** | Informations gĂ©nĂ©rales sur le fonctionnement | DĂ©marrage, requĂȘtes rĂ©ussies | | **WARN** | Situations anormales mais non bloquantes | RequĂȘtes lentes, retries | | **ERROR** | Erreurs qui nĂ©cessitent une attention | Échecs de requĂȘtes, exceptions | | **FATAL** | Erreurs critiques qui arrĂȘtent le service | Paniques, erreurs de configuration | ### 3.3 Champs Obligatoires Tous les logs doivent inclure : - `timestamp` : ISO 8601 - `level` : DEBUG, INFO, WARN, ERROR, FATAL - `message` : Message lisible par un humain - `service` : Nom du service (veza-api, veza-chat, veza-stream, veza-web) - `env` : Environnement (development, staging, production) ### 3.4 Champs de CorrĂ©lation Pour tracer une requĂȘte : - `request_id` : UUID v4 unique par requĂȘte - `trace_id` : ID de trace distribuĂ© (si OpenTelemetry) - `span_id` : ID de span (si OpenTelemetry) ### 3.5 Champs Contextuels Selon le contexte : - `user_id` : ID de l'utilisateur (si authentifiĂ©) - `method`, `path`, `status` : Pour les requĂȘtes HTTP - `latency_ms` : DurĂ©e de la requĂȘte - `ip`, `user_agent` : Informations client --- ## 4. CONFIGURATION UNIFIÉE ### 4.1 Variables d'Environnement ```env # ============================================================================= # LOGGING - Configuration UnifiĂ©e # ============================================================================= # Niveau de log (standardisĂ© pour tous les services) # Valeurs: DEBUG, INFO, WARN, ERROR (Go) ou debug, info, warn, error (Rust) LOG_LEVEL=info # Format des logs # Valeurs: json, text, console LOG_FORMAT=json # AgrĂ©gation de logs (Backend Go uniquement) LOG_AGGREGATION_ENABLED=true LOG_AGGREGATION_ENDPOINT=http://loki:3100/loki/api/v1/push LOG_AGGREGATION_BATCH_SIZE=100 LOG_AGGREGATION_FLUSH_INTERVAL=5s LOG_AGGREGATION_TIMEOUT=10s LOG_AGGREGATION_LABELS=service=veza-api,env=production # Rotation des logs (Services Rust uniquement) LOG_FILE=/var/log/veza/service.log LOG_MAX_SIZE=100MB LOG_MAX_FILES=5 LOG_COMPRESS=true # Sampling (optionnel) LOG_SAMPLING_ENABLED=true LOG_SAMPLING_DEBUG_RATE=10 # 10% des logs DEBUG LOG_SAMPLING_INFO_RATE=50 # 50% des logs INFO ``` ### 4.2 Configuration par Environnement #### Development ```env LOG_LEVEL=DEBUG LOG_FORMAT=console LOG_AGGREGATION_ENABLED=false ``` #### Staging ```env LOG_LEVEL=INFO LOG_FORMAT=json LOG_AGGREGATION_ENABLED=true ``` #### Production ```env LOG_LEVEL=INFO LOG_FORMAT=json LOG_AGGREGATION_ENABLED=true LOG_SAMPLING_ENABLED=true ``` --- ## 5. CORRÉLATION DISTRIBUÉE ### 5.1 Propagation du Request ID #### Backend Go → Services Rust (HTTP) ```go // Dans les appels HTTP vers Rust req.Header.Set("X-Request-ID", requestID) req.Header.Set("X-Trace-ID", traceID) ``` #### Services Rust (RĂ©ception) ```rust // Middleware pour extraire les headers let request_id = headers.get("x-request-id") .and_then(|h| h.to_str().ok()) .map(|s| s.to_string()); let trace_id = headers.get("x-trace-id") .and_then(|h| h.to_str().ok()) .map(|s| s.to_string()); // Utiliser dans les spans let span = tracing::span!( tracing::Level::INFO, "request", request_id = %request_id.unwrap_or_default(), trace_id = %trace_id.unwrap_or_default(), ); ``` #### Backend → Frontend (RĂ©ponses HTTP) ```go // Dans les rĂ©ponses API c.Header("X-Request-ID", requestID) ``` #### Frontend (Extraction) ```typescript // Dans le client API const response = await fetch(url, options); const requestId = response.headers.get('X-Request-ID'); if (requestId) { logger.setRequestId(requestId); } ``` ### 5.2 Propagation via RabbitMQ ```go // Dans les messages RabbitMQ message.Headers["X-Request-ID"] = requestID message.Headers["X-Trace-ID"] = traceID ``` ```rust // Dans les consumers Rust let request_id = headers.get("x-request-id") .and_then(|h| h.as_str()) .map(|s| s.to_string()); ``` --- ## 6. CHECKLIST D'IMPLÉMENTATION ### Phase 1 : Corrections Critiques - [ ] Corriger la configuration du logger Go (LOG_LEVEL) - [ ] Supprimer l'initialisation dupliquĂ©e du logger - [ ] ImplĂ©menter le filtre de secrets - [ ] Remplacer tous les fmt.Println (12 fichiers) - [ ] Supprimer les logs de debug excessifs - [ ] Propager Request ID vers services Rust - [ ] Tester la corrĂ©lation entre services ### Phase 2 : Standardisation - [ ] Unifier la configuration Rust (utiliser veza-common) - [ ] ImplĂ©menter le logger structurĂ© frontend - [ ] Remplacer tous les console.log (192 occurrences) - [ ] Activer l'agrĂ©gation par dĂ©faut en production - [ ] Standardiser les formats de logs (JSON en prod) - [ ] Documenter les standards de logging ### Phase 3 : AmĂ©liorations - [ ] ImplĂ©menter le sampling des logs - [ ] Ajouter l'error tracking frontend (Sentry) - [ ] ImplĂ©menter la rotation des logs (Rust) - [ ] ImplĂ©menter le logging asynchrone - [ ] CrĂ©er des dashboards Grafana - [ ] Tests d'intĂ©gration de la corrĂ©lation ### Validation - [ ] Tester la corrĂ©lation d'une requĂȘte complĂšte (frontend → backend → chat → stream) - [ ] VĂ©rifier que tous les logs sont structurĂ©s (JSON) - [ ] VĂ©rifier que les secrets sont filtrĂ©s - [ ] VĂ©rifier que les logs sont agrĂ©gĂ©s dans Loki - [ ] VĂ©rifier les performances (pas de dĂ©gradation) --- ## 7. MÉTRIQUES DE SUCCÈS ### Avant la Refonte - ❌ Temps pour tracer une requĂȘte : **IMPOSSIBLE** - ❌ Logs structurĂ©s : **~60%** (Go seulement) - ❌ CorrĂ©lation entre services : **0%** - ❌ AgrĂ©gation activĂ©e : **Non** ### AprĂšs la Refonte - ✅ Temps pour tracer une requĂȘte : **< 1 minute** - ✅ Logs structurĂ©s : **100%** - ✅ CorrĂ©lation entre services : **100%** - ✅ AgrĂ©gation activĂ©e : **Oui (prod)** --- **Fin du Plan de Refonte**