# 🏗️ 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<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** :
```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**

