veza/docs/archive/root-md/LOGGING_REFONTE_PLAN.md
senke 43af35fd93 chore(audit 2.2, 2.3): nettoyer .md et .json à la racine
- Archiver 131 .md dans docs/archive/root-md/
- Archiver 22 .json dans docs/archive/root-json/
- Conserver 7 .md utiles (README, CONTRIBUTING, CHANGELOG, etc.)
- Conserver package.json, package-lock.json, turbo.json
- Ajouter README d'index dans chaque archive
2026-02-15 14:35:08 +01:00

19 KiB

🏗️ 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
  2. Plan d'Implémentation
  3. Standards de Logging
  4. Configuration Unifiée
  5. Corrélation Distribuée
  6. Checklist d'Implé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 :

// 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 :

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 :

// 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 :

// Dans chat_service.go
requestID := c.GetString("request_id")
req.Header.Set("X-Request-ID", requestID)

Code 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 :

// 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 :

interface LogEntry {
  level: 'debug' | 'info' | 'warn' | 'error';
  message: string;
  timestamp: string;
  request_id?: string;
  user_id?: string;
  context?: Record<string, unknown>;
}

class StructuredLogger {
  private requestId?: string;
  
  setRequestId(id: string) {
    this.requestId = id;
  }
  
  private log(level: LogEntry['level'], message: string, context?: Record<string, unknown>) {
    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<string, unknown>) {
    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 :

# 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 :

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 :

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)

{
  "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)

{
  "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)

{
  "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

# =============================================================================
# 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

LOG_LEVEL=DEBUG
LOG_FORMAT=console
LOG_AGGREGATION_ENABLED=false

Staging

LOG_LEVEL=INFO
LOG_FORMAT=json
LOG_AGGREGATION_ENABLED=true

Production

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)

// Dans les appels HTTP vers Rust
req.Header.Set("X-Request-ID", requestID)
req.Header.Set("X-Trace-ID", traceID)

Services Rust (Réception)

// 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)

// Dans les réponses API
c.Header("X-Request-ID", requestID)

Frontend (Extraction)

// 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

// Dans les messages RabbitMQ
message.Headers["X-Request-ID"] = requestID
message.Headers["X-Trace-ID"] = traceID
// 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