660 lines
19 KiB
Markdown
660 lines
19 KiB
Markdown
|
|
# 🏗️ 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**
|
||
|
|
|