📖 Manuel
Log Analyzer
Workflow
1. Identifier le format et la source
Avant tout filtrage, qualifier le format :
| Format | Outil natif | Champ clé à valider |
|---|---|---|
| JSON structuré | jq, Kibana, Loki | timestamp, level, traceId |
| Plain text (Apache/Nginx/IIS) | grep, awk, goaccess | pattern de date, status code |
| Syslog RFC 5424 | journalctl, ELK Filebeat | PRI, HOSTNAME, MSGID |
| Windows Event Log | Get-WinEvent, Winlogbeat | EventID, Provider |
| Application Insights / Datadog | KQL, SDK query | operation_Id, customDimensions |
# Détecter rapidement le format
head -5 app.log | python3 -m json.tool 2>/dev/null || echo "plain text"
2. Filtrer par fenêtre temporelle et correlation ID
Règle d'or : isoler avant d'analyser.
# Plage horaire + niveau ERROR dans un fichier plain text
awk '/2026-06-24T10:[23][0-9]/ && /ERROR/' app.log
# JSON structuré avec jq
jq 'select(.level=="Error" and .timestamp >= "2026-06-24T10:20:00Z")' app.log
# journalctl (systemd)
journalctl -u myservice --since "2026-06-24 10:00" --until "2026-06-24 11:00" -p err
# grep avec correlation ID (format UUID)
grep "traceId=3fa85f64" app.log | sort -t' ' -k1
// Application Insights — top 10 exceptions sur 1h
exceptions
| where timestamp > ago(1h)
| summarize count() by type, outerMessage
| order by count_ desc
| take 10
# Grafana Loki — error rate par service
sum(rate({app="api"} |= "ERROR" [5m])) by (service)
3. Identifier les patterns d'erreurs
Objectifs : top exceptions, spikes, cascading failures.
# Top 10 messages d'erreur (plain text)
grep "ERROR" app.log | sed 's/.*ERROR //' | sort | uniq -c | sort -rn | head 10
# Fréquence d'erreurs par minute (détecter les spikes)
grep "ERROR" app.log | awk '{print $1}' | cut -c1-16 | uniq -c
# Détecter une exception Java/C# récurrente
grep -A 5 "Unhandled exception" app.log | grep "at " | sort | uniq -c | sort -rn
Critères de sévérité :
- Spike soudain (×5 erreurs en 2 min) → incident actif, vérifier les dépendances.
- Erreur unique répétée > 100×/min → bug code ou config, pas infra.
- Erreurs croissantes en escalier → memory leak ou pool exhaustion.
- Erreurs aléatoires faible fréquence → instabilité réseau ou 3rd party.
4. Corréler entre services (distributed tracing)
# Suivre un traceId à travers plusieurs fichiers de services
grep "traceId=abc123" service-a.log service-b.log service-c.log | sort -k1
# Reconstituer la timeline d'une transaction
grep "abc123" *.log | awk '{print $1, $2, $NF}' | sort
// Application Insights — trace complète d'une opération
union requests, dependencies, exceptions, traces
| where operation_Id == "abc123def456"
| order by timestamp asc
| project timestamp, itemType, name, duration, success, message
Si pas de traceId : corréler par userId + fenêtre de ±500 ms, ou par sessionId.
5. Analyser les performances
# Extraire les durées (format "duration=NNNms") et calculer P95
grep "duration=" app.log | grep -oP 'duration=\K[0-9]+' | sort -n | \
awk 'BEGIN{c=0} {a[c++]=$1} END{print "P50=" a[int(c*0.5)] " P95=" a[int(c*0.95)] " P99=" a[int(c*0.99)]}'
# Identifier les slow SQL queries (EF Core / SqlClient)
grep "Executed DbCommand" app.log | grep -oP '\(\K[0-9]+(?=ms\))' | awk '$1>1000' | wc -l
// Application Insights — P95 latence par endpoint
requests
| where timestamp > ago(1h)
| summarize percentile(duration, 95) by name
| order by percentile_duration_95 desc
6. Structured logging — configuration minimale
Serilog (.NET) :
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.Enrich.WithProperty("Service", "MyApi")
.Enrich.WithProperty("Version", "1.4.2")
.WriteTo.Console(new JsonFormatter())
.WriteTo.Seq("http://seq:5341")
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.CreateLogger();
// Usage — toujours des propriétés structurées, jamais de string interpolation
Log.Warning("Paiement échoué {TransactionId} pour {UserId}", txId, userId);
Winston (Node.js) :
const logger = winston.createLogger({
format: winston.format.combine(winston.format.timestamp(), winston.format.json()),
defaultMeta: { service: 'payment-api', version: '2.1.0' },
transports: [new winston.transports.Console()]
});
logger.error('Payment failed', { transactionId: txId, userId, amount });
7. Centralisation — choix d'infrastructure
| Stack | Quand choisir | Complexité |
|---|---|---|
| ELK (Elasticsearch + Filebeat + Kibana) | Volume > 10 GB/jour, full-text search | Haute |
| Grafana Loki + Promtail | Déjà sur Grafana/Prometheus, volume modéré | Moyenne |
| Application Insights (Azure) | App Azure, SDK .NET/Node/Python | Faible |
| CloudWatch Logs Insights | Infra AWS Lambda/ECS/EC2 | Faible |
| Seq | Dev/staging .NET, budget limité | Très faible |
8. Alertes sur les logs
// Application Insights — alerter si error rate > 1% sur 5 min
requests
| where timestamp > ago(5m)
| summarize failed=countif(success==false), total=count()
| where (failed * 100 / total) > 1
# Grafana Loki alert rule
- alert: HighErrorRate
expr: sum(rate({app="api"} |= "ERROR" [5m])) > 10
for: 2m
annotations:
summary: "Plus de 10 erreurs/s depuis 2 min"
Garde-fous et anti-patterns
Ne jamais faire :
- Logger des mots de passe, tokens, numéros de carte, PII — même en debug.
- Utiliser
string.Formatou interpolation dans les messages Serilog (perd la structure). - Logger chaque itération d'une boucle sans rate limiting → log flooding → I/O saturation.
- Ignorer les
WARN: ils signalent souvent les prémices d'une panne. - Comparer des logs sur des fuseaux horaires différents sans normaliser en UTC.
Pièges courants :
- Log rotation silencieuse : le fichier actif est
/app.log, mais l'erreur cherchée est dans/app.log.1de la veille. - Buffering async : Serilog/NLog avec sink asynchrone peut perdre les derniers logs avant un crash — toujours configurer
flushToDiskIntervaloubufferedBatching: falseen prod critique. - Correlation ID absent : sans traceId, la corrélation multi-service est impossible a posteriori. L'ajouter en middleware dès le début.
- Timestamps locaux : toujours écrire en UTC (
DateTimeOffset.UtcNow), afficher en local uniquement dans l'UI. - Over-logging en INFO : noie les vrais signaux ; réserver INFO aux events métier significatifs, pas aux HTTP 200 routiniers.
Bonnes pratiques 2026
- OpenTelemetry Logs est désormais GA : privilégier
OTLPcomme protocole d'export pour unifier traces, métriques et logs dans un seul pipeline. - Log sampling : en haute fréquence (> 10k req/s), logger 100% des ERROR/WARN, échantillonner les INFO à 10%.
- Structured query first : toujours préférer une requête KQL/LogQL/Lucene à un
grepsur des fichiers bruts en production — plus rapide et moins risqué pour la charge I/O. - Retention tiered : ERROR → 90 jours, INFO → 30 jours, DEBUG → 7 jours max. Réduire les coûts de stockage ELK/Loki de 60–70%.
- Masking automatique : intégrer un enrichisseur Serilog ou un Logstash filter qui masque les patterns PAN (cartes), IBAN, emails avant l'indexation.