Production - Observabilite et performance
Apprendre les essentiels production pour services Java: logs structures, metriques, tracing, profiling et methode de tuning performance.
Pourquoi cette etape est importante
Si tu ne peux pas observer ton systeme, tu ne peux pas l'exploiter de facon fiable. En production, la vitesse de diagnostic compte autant que la vitesse d'execution.
Logs structures
Prefere des logs structures au texte libre. Inclure des champs stables comme :
- timestamp
- level
- nom du service
- request id / trace id
- user id ou tenant id si pertinent
- nom d'evenement metier (ex:
order_created)
Cela accelere la recherche, la correlation et l'alerting.
Comment faire de bons logs
Un bon log repond vite a 4 questions :
- quoi s'est passe ?
- sur quelle entite ?
- avec quel contexte ?
- avec quel niveau de gravite ?
Regles pratiques :
- loguer des evenements, pas des romans
- garder des messages courts et stables
- ajouter les variables utiles en champs (id, status, duree)
- ne jamais loguer des secrets (mot de passe, token brut, carte)
- logger les exceptions avec stack trace uniquement quand utile
- utiliser
INFOpour metier normal,WARNpour degration,ERRORpour echec
Dependances de logs (Java)
Stack recommandee (simple et robuste):
- API: SLF4J
- implementation: Logback
- option JSON: logstash-logback-encoder
Maven
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.16</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>8.0</version>
</dependency>
</dependencies>
Gradle
dependencies {
implementation 'org.slf4j:slf4j-api:2.0.16'
runtimeOnly 'ch.qos.logback:logback-classic:1.5.8'
runtimeOnly 'net.logstash.logback:logstash-logback-encoder:8.0'
}
Important: evite d'avoir plusieurs bindings SLF4J en meme temps (sinon logs incoherents et warnings au demarrage).
Exemple de configuration Logback (texte)
Fichier logback.xml minimal avec correlation id via MDC :
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} trace=%X{traceId} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Exemple de configuration JSON
Pour envoyer facilement les logs vers Loki/ELK/OpenSearch :
<configuration>
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<root level="INFO">
<appender-ref ref="JSON" />
</root>
</configuration>
Correlation ID (MDC)
Le but: rattacher tous les logs d'une requete a un meme identifiant.
import org.slf4j.MDC;
public void handleRequest(String traceId) {
MDC.put("traceId", traceId);
try {
logger.info("request_started path=/api/orders");
// traitement
logger.info("request_completed status=200");
} finally {
MDC.remove("traceId");
}
}
Exemples de logs: mauvais vs bons
Mauvais:
logger.info("ca marche");
logger.error("erreur");
Bons:
logger.info("order_created orderId={} customerId={} amount={} currency={}",
orderId, customerId, amount, currency);
logger.warn("payment_retry orderId={} attempt={} reason={}",
orderId, attempt, reason);
logger.error("payment_failed orderId={} provider={} traceId={}",
orderId, provider, traceId, ex);
Niveau de logs: regle simple
DEBUG: details techniques pour debug local/stagingINFO: evenements metier normauxWARN: anomalie recuperable ou degradationERROR: echec fonctionnel/technique a traiter
Metriques et tracing
Les metriques repondent a: "que se passe-t-il ?" Le tracing repond a: "ou est depense le temps a travers les services ?"
Metriques de base:
- taux de requetes
- percentiles de latence (p50/p95/p99)
- taux d'erreur
- memoire JVM/GC
Approche alerting
Alerter sur les symptomes qui impactent les utilisateurs, pas sur des evenements techniques bruyants. Definir des seuils orientes SLO quand possible.
Workflow de profiling et tuning
- mesurer la base
- identifier le goulot
- changer une chose
- mesurer a nouveau
Ne jamais optimiser a l'aveugle.
Goulots classiques
- trop d'aller-retours base de donnees
- I/O bloquante sous forte charge
- pression allocation/GC elevee
- algorithmes inefficaces sur chemins chauds
Erreurs frequentes
- pas de correlation id dans les logs
- metriques collectees mais non exploitees en dashboard/alertes
- tuning JVM sans preuve
- micro-optimisations avant correction des hotspots d'architecture
A retenir
- Des logs exploitables accelerent directement le diagnostic production
- Standardiser stack + format de logs (SLF4J + Logback + JSON si besoin)
- Correlation ID obligatoire pour les APIs
- Utiliser logs, metriques et traces ensemble
- Tuner la performance via une boucle de mesure, pas a l'intuition