Observabilité Spring Boot en 2026 : OpenTelemetry, Tracing Distribué et Questions d'Entretien
Guide complet sur l'observabilité Spring Boot avec OpenTelemetry et le tracing distribué. Configuration OTLP, Observation API, propagation de contexte et questions techniques pour entretiens backend.

L'observabilité Spring Boot combine la journalisation, les métriques et le tracing distribué dans un système unifié qui révèle comment les requêtes traversent les microservices. Depuis Spring Boot 3, le framework a adopté Micrometer Tracing (remplaçant Spring Cloud Sleuth) et introduit l'Observation API, qui fournit un point d'instrumentation unique émettant à la fois des métriques et des traces.
Spring Boot recommande d'utiliser Observation.observe() plutôt que d'appeler OpenTelemetry directement. Un seul appel d'instrumentation produit des métriques via Micrometer et des traces via le bridge OpenTelemetry, réduisant la duplication de code et assurant une nomenclature cohérente des tags entre les signaux.
Comment l'Observation API Fait le Pont entre Micrometer et OpenTelemetry
L'Observation API agit comme une façade sur les métriques et le tracing. Quand le code appelle Observation.createNotStarted(), Spring Boot route l'observation vers les handlers enregistrés : MeterObservationHandler pour les métriques Micrometer et TracingObservationHandler pour les traces distribuées. Cette architecture signifie qu'une seule instrumentation exporte partout.
La dépendance bridge micrometer-tracing-bridge-otel connecte Micrometer Tracing au SDK OpenTelemetry. Les traces passent par le SdkTracerProvider d'OpenTelemetry et s'exportent via OTLP vers des backends comme Jaeger, Tempo, ou tout collecteur compatible OpenTelemetry.
@Configuration
public class ObservabilityConfig {
@Bean
public ObservationRegistryCustomizer<ObservationRegistry> addLowCardinalityTags() {
return registry -> registry.observationConfig()
.observationHandler(new ObservationTextPublisher()); // Logs observations to console
}
}La configuration ci-dessus enregistre un ObservationHandler qui journalise chaque observation. En production, les handlers auto-configurés envoient les données aux registres Micrometer et aux exporteurs OpenTelemetry sans code supplémentaire.
Dépendances Requises pour le Tracing Spring Boot 3.4
Spring Boot 3.4 nécessite des dépendances explicites pour le tracing distribué. Le spring-boot-starter-actuator fournit l'Observation API, mais le tracing requiert le bridge OpenTelemetry et un exporteur.
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
</dependencies>L'artefact micrometer-tracing-bridge-otel fait le pont entre Micrometer Tracing et l'API OpenTelemetry. L'opentelemetry-exporter-otlp envoie les spans aux collecteurs via le protocole OTLP sur HTTP ou gRPC. Spring Boot gère l'alignement des versions via sa gestion des dépendances.
Configuration de l'Export OTLP vers Jaeger ou Tempo
Spring Boot auto-configure un OtlpHttpSpanExporter quand la dépendance OTLP est présente. L'exporteur envoie les traces au endpoint spécifié dans application.yml.
# application.yml
management:
tracing:
sampling:
probability: 1.0 # 100% sampling for dev, reduce in production
otlp:
tracing:
endpoint: http://localhost:4318/v1/traces
opentelemetry:
resource-attributes:
service.name: order-service
deployment.environment: stagingLes resource-attributes attachent des métadonnées à chaque span. Les backends comme Grafana Tempo et Jaeger utilisent ces attributs pour grouper les traces par service et environnement. Définir sampling.probability à 1.0 capture toutes les requêtes en développement, mais les systèmes de production échantillonnent typiquement entre 1% et 10% pour contrôler les coûts de stockage.
Créer des Observations Personnalisées avec Tags de Cardinalité Basse et Haute
Les observations supportent deux types de tags : cardinalité basse pour les métriques (valeurs bornées comme les méthodes HTTP ou codes de statut) et cardinalité haute pour les traces (valeurs non bornées comme les IDs utilisateur ou IDs de requête).
@Service
public class PaymentService {
private final ObservationRegistry observationRegistry;
public PaymentService(ObservationRegistry observationRegistry) {
this.observationRegistry = observationRegistry;
}
public PaymentResult processPayment(PaymentRequest request) {
return Observation.createNotStarted("payment.process", observationRegistry)
.lowCardinalityKeyValue("payment.method", request.getMethod().name()) // enum: CARD, BANK_TRANSFER
.lowCardinalityKeyValue("currency", request.getCurrency()) // bounded: USD, EUR, GBP
.highCardinalityKeyValue("payment.id", request.getPaymentId()) // unique per request
.highCardinalityKeyValue("customer.id", request.getCustomerId()) // high cardinality
.observe(() -> executePayment(request));
}
private PaymentResult executePayment(PaymentRequest request) {
// Payment gateway call
return new PaymentResult(true, "TXN-" + UUID.randomUUID());
}
}Les tags de cardinalité basse apparaissent dans les métriques et les traces. Les tags de cardinalité haute n'apparaissent que dans les traces car les métriques avec des dimensions non bornées explosent le stockage. Cette distinction prévient les bombes de cardinalité Prometheus tout en gardant les traces riches en détails.
Les recruteurs demandent souvent pourquoi certains tags doivent être exclus des métriques. La réponse concerne la cardinalité : un tag d'ID utilisateur sur une métrique crée une série temporelle par utilisateur, potentiellement des millions de séries. Les backends de monitoring peinent avec la haute cardinalité, menant à l'épuisement mémoire et des requêtes lentes. Les traces gèrent la haute cardinalité via l'échantillonnage, en faisant le bon endroit pour les identifiants spécifiques aux requêtes.
Prêt à réussir tes entretiens Spring Boot ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
Propagation du Contexte de Trace à Travers les Frontières Asynchrones
Le tracing distribué nécessite la propagation de contexte. Les headers HTTP transportent les IDs de trace entre services, mais les opérations asynchrones comme les méthodes @Async ou les chaînes CompletableFuture peuvent perdre le contexte si mal configurées.
Spring Boot 3.4 fournit une propagation automatique pour les flux réactifs et les méthodes @Async quand configuré :
# application.yml
spring:
reactor:
context-propagation: auto
task:
execution:
propagate-context: truePour les beans TaskExecutor personnalisés, les envelopper avec ContextPropagatingTaskDecorator :
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setTaskDecorator(new ContextPropagatingTaskDecorator());
executor.initialize();
return executor;
}
}Le décorateur copie l'Observation courante et le contexte de trace dans le thread asynchrone. Sans lui, les spans démarrés dans les tâches async n'ont pas de parent, cassant l'arbre de trace.
Utilisation des Annotations @Observed et @NewSpan
Spring Boot 3.4 supporte l'observabilité déclarative via les annotations. Activer le traitement des annotations en ajoutant le weaver AspectJ :
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency># application.yml
management:
observations:
annotations:
enabled: true@Service
public class InventoryService {
@Observed(name = "inventory.check", contextualName = "checkStock")
public StockLevel checkStock(String productId) {
// Database query
return new StockLevel(productId, 42);
}
@NewSpan("inventory.reserve") // Creates a child span under the current trace
public ReservationResult reserveStock(String productId, int quantity) {
// Update inventory
return new ReservationResult(true, "RES-" + System.currentTimeMillis());
}
}@Observed crée à la fois une métrique (timer) et un span. @NewSpan ne crée qu'un span, utile quand l'opération a déjà des métriques ailleurs. Les deux annotations gèrent automatiquement l'enregistrement des exceptions et le statut du span.
Composants Auto-Instrumentés dans Spring Boot 3.4
Spring Boot 3.4 auto-instrumente plusieurs composants sans changement de code :
| Composant | Nom d'Observation | Ce Qui Est Capturé |
|---|---|---|
| Spring MVC | http.server.requests | Chemin de requête, méthode, statut, exception |
| WebClient | http.client.requests | Appels HTTP sortants |
| RestClient | http.client.requests | Appels HTTP sortants (Spring 6.1+) |
| Spring Kafka | spring.kafka.listener | Groupe de consommateurs, topic, partition |
| Spring Data JPA | spring.data.repository | Méthode du repository, temps de requête |
| Tâches Planifiées | spring.scheduling | Nom de la tâche, temps d'exécution |
La documentation Spring Boot Actuator liste tous les composants auto-instrumentés. Les bibliothèques tierces comme Datasource Micrometer ajoutent le tracing des requêtes JDBC.
Filtrer les Observations pour Réduire le Bruit
Les health checks, les probes de disponibilité et les ressources statiques génèrent du bruit dans les traces. Les filtrer avec des prédicats :
@Configuration
public class ObservationFilterConfig {
@Bean
public ObservationPredicate noHealthChecks() {
return (name, context) -> !name.equals("http.server.requests")
|| !isHealthEndpoint(context);
}
private boolean isHealthEndpoint(Observation.Context context) {
if (context instanceof ServerRequestObservationContext http) {
String path = http.getCarrier().getRequestURI();
return path.startsWith("/actuator/health") || path.startsWith("/actuator/ready");
}
return false;
}
}Alternativement, désactiver les observations par nom dans la configuration :
# application.yml
management:
observations:
enable:
spring.security: false # Disable Spring Security observations
http.server.requests.actuator: false # Custom predicate nameL'échantillonnage réduit le pourcentage de traces collectées sur tous les endpoints. Le filtrage supprime complètement des endpoints spécifiques. Utiliser le filtrage pour les endpoints qui ne fournissent jamais de valeur diagnostique (health checks, scraping de métriques). Utiliser l'échantillonnage pour contrôler les coûts tout en préservant des données représentatives.
Corrélation des Logs avec les IDs de Trace
Spring Boot 3.4 ajoute automatiquement les IDs de trace et de span au MDC (Mapped Diagnostic Context) quand Micrometer Tracing est actif. Logback et Log4j2 peuvent inclure ces IDs dans la sortie des logs :
<!-- logback-spring.xml -->
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - traceId=%X{traceId} spanId=%X{spanId} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</configuration>Avec ce pattern, chaque ligne de log inclut l'ID de trace. Rechercher les logs par ID de trace retourne tous les messages d'une seule requête à travers tous les services, corrélant les logs avec les spans dans Jaeger ou Tempo.
Questions d'Entretien Courantes sur l'Observabilité Spring Boot
Les recruteurs évaluent à la fois la compréhension conceptuelle et l'expérience pratique de l'observabilité. Ces questions apparaissent fréquemment pour les postes backend senior.
Q : Quelle est la différence entre Micrometer Tracing et OpenTelemetry ?
Micrometer Tracing est une API vendor-neutre pour le tracing distribué, similaire à la façon dont Micrometer abstrait les métriques. OpenTelemetry est une implémentation et un protocole spécifiques. Spring Boot utilise Micrometer Tracing comme API et fait le pont vers OpenTelemetry pour l'export via micrometer-tracing-bridge-otel. Cette couche permet aux applications de changer de backend sans modification de code.
Q : Pourquoi Spring recommande-t-il l'Observation API plutôt que l'instrumentation OpenTelemetry directe ?
L'Observation API fournit un point d'instrumentation unique qui émet à la fois des métriques et des traces. Les appels OpenTelemetry directs ne produisent que des traces. Utiliser Observation.observe() génère une métrique timer et un span depuis le même code, réduisant la duplication et assurant une nomenclature cohérente.
Q : Comment débugger une trace qui montre un écart entre les spans ?
Les écarts indiquent une instrumentation manquante ou une propagation de contexte cassée. Vérifier si le code utilise des opérations async sans ContextPropagatingTaskDecorator. Vérifier que les clients HTTP sont instrumentés (WebClient, RestClient, ou un RestTemplate manuellement enveloppé). Pour les files de messages, confirmer que les headers de trace se propagent à travers les propriétés des messages.
Q : Que se passe-t-il si on ajoute un tag de haute cardinalité à une métrique ?
Chaque valeur de tag unique crée une nouvelle série temporelle. Un tag d'ID utilisateur avec des millions d'utilisateurs crée des millions de séries, épuisant la mémoire dans Prometheus ou d'autres backends TSDB. La solution est d'utiliser highCardinalityKeyValue() au lieu de lowCardinalityKeyValue(), ce qui n'ajoute le tag qu'aux traces où la haute cardinalité est attendue.
Construire un Pipeline de Tracing Distribué avec Docker Compose
Une stack d'observabilité locale aide à valider l'instrumentation avant le déploiement en production. Cet exemple utilise le OpenTelemetry Collector et Jaeger :
# docker-compose.yml
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:0.102.0
command: ["--config", "/etc/otel-collector-config.yml"]
volumes:
- ./otel-collector-config.yml:/etc/otel-collector-config.yml
ports:
- "4318:4318" # OTLP HTTP
- "4317:4317" # OTLP gRPC
jaeger:
image: jaegertracing/jaeger:2.3
ports:
- "16686:16686" # UI
environment:
- COLLECTOR_OTLP_ENABLED=true
app:
build: .
environment:
- MANAGEMENT_OTLP_TRACING_ENDPOINT=http://otel-collector:4318/v1/traces
depends_on:
- otel-collector# otel-collector-config.yml
receivers:
otlp:
protocols:
http:
endpoint: 0.0.0.0:4318
grpc:
endpoint: 0.0.0.0:4317
processors:
batch:
timeout: 1s
send_batch_size: 1024
exporters:
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp/jaeger]Le collecteur reçoit les spans de l'application Spring Boot et les transmet à Jaeger. Cette architecture permet d'ajouter des exporteurs (Tempo, Zipkin, backends cloud) sans changer le code applicatif.
Passe à la pratique !
Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.
Points Clés pour l'Observabilité Spring Boot
- L'Observation API unifie métriques et tracing : un seul appel
Observation.observe()produit à la fois une métrique timer et un span, éliminant la double instrumentation. - Ajouter
micrometer-tracing-bridge-oteletopentelemetry-exporter-otlppour activer l'export OTLP. Spring Boot 3.4 auto-configure l'exporteur HTTP avec le endpoint demanagement.otlp.tracing.endpoint. - Utiliser
lowCardinalityKeyValue()pour les dimensions apparaissant dans les métriques (valeurs bornées). UtiliserhighCardinalityKeyValue()pour les données uniquement dans les traces (IDs utilisateur, IDs de requête). - Activer la propagation de contexte pour le code async avec
spring.task.execution.propagate-context=trueet envelopper les executors personnalisés avecContextPropagatingTaskDecorator. - Filtrer les endpoints bruyants comme
/actuator/healthavec des beansObservationPredicatepour garder les traces focalisées sur les opérations métier. - Les patterns de log doivent inclure
%X{traceId}pour corréler les lignes de log avec les traces distribuées dans le backend d'observabilité. - Les questions d'entretien sur l'observabilité se concentrent sur la cardinalité, les échecs de propagation de contexte, et la distinction entre l'Observation API et l'utilisation directe d'OpenTelemetry.
Tu saurais repérer le bug en Spring Boot ?
Un vrai bout de code, un bug caché, une tentative par jour. Sans compte pour essayer.

Écrit par
Anthony Fillion-MailletFondateur de SharpSkill
Développeur fullstack depuis plus de 10 ans. Il dirige SharpSkill et répond de tout ce qui y est publié.
Mis à jour le 19 septembre 2026
Tags
Partager
Articles similaires

Spring Boot Actuator : monitoring production avec Micrometer et Prometheus
Guide complet Spring Boot Actuator pour le monitoring en production. Configuration Micrometer, métriques Prometheus, endpoints personnalisés et alerting.

Spring Boot YAML vs Properties : Comparaison des Configurations et Questions d'Entretien 2026
Comparaison approfondie entre YAML et Properties dans Spring Boot. Syntaxe, gestion des profils, structures de donnees et questions d'entretien technique.

Logs structurés Spring Boot en 2026 : JSON pour la production avec Logback et OpenTelemetry
Guide complet des logs structurés Spring Boot 4.x. Support JSON natif, starter OpenTelemetry, tracing MDC et intégration ELK Stack pour l'observabilité en production.