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 Actuator monitoring avec Micrometer et Prometheus

Spring Boot Actuator transforme le monitoring des applications Java en fournissant des endpoints prêts à l'emploi pour la santé, les métriques et le diagnostic. Combiné avec Micrometer et Prometheus, il offre une solution complète d'observabilité pour les environnements de production.

Point clé

Actuator expose automatiquement plus de 50 métriques JVM et applicatives. Micrometer sert de façade pour publier ces métriques vers Prometheus, Grafana, Datadog ou tout autre système de monitoring.

Configuration de base avec Spring Boot 3

Dépendances Maven requises

L'intégration d'Actuator avec Prometheus nécessite trois dépendances principales. Le starter Actuator active les endpoints, Micrometer fournit l'abstraction des métriques, et le registry Prometheus formate les données pour le scraping.

xml
<!-- pom.xml -->
<!-- Configuration Actuator + Micrometer + Prometheus -->
<dependencies>
    <!-- Spring Boot Actuator - endpoints de monitoring -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <!-- Micrometer Registry Prometheus -->
    <!-- Expose les métriques au format Prometheus -->
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>

    <!-- AOP pour les métriques @Timed et @Counted -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
</dependencies>

Ces dépendances suffisent pour exposer un endpoint /actuator/prometheus que Prometheus peut scraper périodiquement.

Configuration des endpoints Actuator

Par défaut, seuls les endpoints health et info sont exposés via HTTP. La configuration explicite contrôle quels endpoints restent accessibles en production.

yaml
# application.yml
# Configuration Actuator pour la production
management:
  endpoints:
    web:
      exposure:
        # Endpoints exposés via HTTP
        # health, info, prometheus sont les minimums pour le monitoring
        include: health,info,prometheus,metrics,env,loggers
      base-path: /actuator
    # Désactive les endpoints non utilisés pour réduire la surface d'attaque
    enabled-by-default: false
  endpoint:
    # Active individuellement chaque endpoint nécessaire
    health:
      enabled: true
      show-details: when-authorized
      show-components: when-authorized
    info:
      enabled: true
    prometheus:
      enabled: true
    metrics:
      enabled: true
    env:
      enabled: true
      # Masque les valeurs sensibles
      show-values: when-authorized
    loggers:
      enabled: true

L'option show-details: when-authorized affiche les détails de santé uniquement aux utilisateurs authentifiés avec le rôle approprié.

ActuatorSecurityConfig.javajava
// Sécurisation des endpoints Actuator
package com.example.monitoring.config;

import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class ActuatorSecurityConfig {

    @Bean
    SecurityFilterChain actuatorSecurityFilterChain(HttpSecurity http) throws Exception {
        return http
            .securityMatcher(EndpointRequest.toAnyEndpoint())
            .authorizeHttpRequests(auth -> auth
                // Health et info publics pour les load balancers
                .requestMatchers(EndpointRequest.to("health", "info")).permitAll()
                // Prometheus accessible depuis le réseau interne
                .requestMatchers(EndpointRequest.to("prometheus")).hasIpAddress("10.0.0.0/8")
                // Autres endpoints réservés aux admins
                .anyRequest().hasRole("ACTUATOR_ADMIN")
            )
            .httpBasic(basic -> {})
            .build();
    }
}

Cette configuration autorise l'accès public aux endpoints de base tout en protégeant les endpoints sensibles.

Métriques personnalisées avec Micrometer

Compteurs et jauges applicatives

Micrometer fournit plusieurs types de métriques adaptés à différents cas d'usage. Les compteurs mesurent les événements cumulatifs, les jauges les valeurs instantanées, et les timers la durée des opérations.

OrderMetricsService.javajava
// Service de métriques métier personnalisées
package com.example.monitoring.metrics;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import org.springframework.stereotype.Service;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;

@Service
public class OrderMetricsService {

    // Compteur de commandes créées avec tag de statut
    private final Counter ordersCreatedCounter;
    // Timer pour mesurer la durée de traitement
    private final Timer orderProcessingTimer;
    // Valeur atomique pour la jauge des commandes en cours
    private final AtomicInteger pendingOrdersCount = new AtomicInteger(0);

    public OrderMetricsService(MeterRegistry registry) {
        // Compteur avec tags pour filtrer dans Prometheus
        this.ordersCreatedCounter = Counter.builder("orders.created.total")
            .description("Nombre total de commandes créées")
            .tag("application", "order-service")
            .register(registry);

        // Timer avec histogramme pour les percentiles
        this.orderProcessingTimer = Timer.builder("orders.processing.duration")
            .description("Durée de traitement des commandes")
            .publishPercentiles(0.5, 0.95, 0.99)
            .publishPercentileHistogram()
            .register(registry);

        // Jauge liée à une valeur atomique
        // Se met à jour automatiquement à chaque scraping
        Gauge.builder("orders.pending.count", pendingOrdersCount, AtomicInteger::get)
            .description("Nombre de commandes en attente de traitement")
            .register(registry);
    }

    public void recordOrderCreated() {
        ordersCreatedCounter.increment();
        pendingOrdersCount.incrementAndGet();
    }

    public void recordOrderProcessed(Runnable processingLogic) {
        // Mesure automatiquement la durée d'exécution
        orderProcessingTimer.record(processingLogic);
        pendingOrdersCount.decrementAndGet();
    }

    public <T> T recordOrderProcessedWithResult(Supplier<T> processingLogic) {
        return orderProcessingTimer.record(processingLogic);
    }
}

L'utilisation de tags permet de filtrer et agréger les métriques dans Prometheus avec des requêtes PromQL précises.

Annotations @Timed et @Counted

Pour éviter le code boilerplate, Micrometer propose des annotations AOP qui instrumentent automatiquement les méthodes.

PaymentService.javajava
// Instrumentation automatique avec annotations
package com.example.monitoring.service;

import io.micrometer.core.annotation.Counted;
import io.micrometer.core.annotation.Timed;
import org.springframework.stereotype.Service;

@Service
public class PaymentService {

    // @Timed crée automatiquement un Timer
    // Mesure chaque appel et publie count, sum, max
    @Timed(
        value = "payment.process.duration",
        description = "Durée de traitement des paiements",
        percentiles = {0.5, 0.95, 0.99},
        histogram = true
    )
    public PaymentResult processPayment(PaymentRequest request) {
        // Logique de paiement
        validatePayment(request);
        return executePayment(request);
    }

    // @Counted incrémente un compteur à chaque appel
    // Utile pour les événements discrets
    @Counted(
        value = "payment.refunds.total",
        description = "Nombre total de remboursements"
    )
    public void refundPayment(String transactionId) {
        // Logique de remboursement
    }

    // Combinaison des deux annotations
    @Timed(value = "payment.validation.duration")
    @Counted(value = "payment.validation.total")
    private void validatePayment(PaymentRequest request) {
        // Validation du paiement
    }
}
TimedAspectConfig.javajava
// Configuration requise pour activer @Timed
package com.example.monitoring.config;

import io.micrometer.core.aop.CountedAspect;
import io.micrometer.core.aop.TimedAspect;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TimedAspectConfig {

    // Aspect nécessaire pour que @Timed fonctionne
    @Bean
    TimedAspect timedAspect(MeterRegistry registry) {
        return new TimedAspect(registry);
    }

    // Aspect pour @Counted
    @Bean
    CountedAspect countedAspect(MeterRegistry registry) {
        return new CountedAspect(registry);
    }
}
Limitation AOP

Les annotations @Timed et @Counted ne fonctionnent que sur les beans Spring et les appels externes. Les appels internes à une même classe contournent le proxy AOP et ne sont pas instrumentés.

Prêt à réussir tes entretiens Spring Boot ?

Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.

Endpoints de santé personnalisés

Health Indicators métier

Les Health Indicators permettent de vérifier l'état de dépendances externes et de composants métier critiques. Spring Boot fournit des indicateurs par défaut pour la base de données, Redis, et d'autres services courants.

PaymentGatewayHealthIndicator.javajava
// Indicateur de santé pour la gateway de paiement
package com.example.monitoring.health;

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;

import java.time.Duration;
import java.time.Instant;

@Component
public class PaymentGatewayHealthIndicator implements HealthIndicator {

    private final RestClient restClient;
    private final String gatewayHealthUrl;

    public PaymentGatewayHealthIndicator(RestClient.Builder restClientBuilder) {
        this.restClient = restClientBuilder.build();
        this.gatewayHealthUrl = "https://api.payment-gateway.com/health";
    }

    @Override
    public Health health() {
        Instant start = Instant.now();

        try {
            // Appel au endpoint de santé de la gateway
            var response = restClient.get()
                .uri(gatewayHealthUrl)
                .retrieve()
                .toBodilessEntity();

            Duration responseTime = Duration.between(start, Instant.now());

            if (response.getStatusCode().is2xxSuccessful()) {
                return Health.up()
                    .withDetail("responseTime", responseTime.toMillis() + "ms")
                    .withDetail("statusCode", response.getStatusCode().value())
                    .build();
            } else {
                return Health.down()
                    .withDetail("statusCode", response.getStatusCode().value())
                    .withDetail("reason", "Unexpected status code")
                    .build();
            }
        } catch (Exception e) {
            Duration responseTime = Duration.between(start, Instant.now());

            return Health.down()
                .withDetail("error", e.getClass().getSimpleName())
                .withDetail("message", e.getMessage())
                .withDetail("responseTime", responseTime.toMillis() + "ms")
                .build();
        }
    }
}

Cet indicateur apparaît automatiquement dans /actuator/health sous le nom paymentGateway.

Health Groups pour Kubernetes

Les groupes de santé permettent de créer des endpoints distincts pour les probes Kubernetes liveness et readiness.

yaml
# application.yml
# Configuration des groupes de santé pour Kubernetes
management:
  endpoint:
    health:
      group:
        # Liveness probe - l'application est-elle vivante ?
        liveness:
          include: livenessState
          show-details: always
        # Readiness probe - l'application peut-elle recevoir du trafic ?
        readiness:
          include: readinessState,db,redis,paymentGateway
          show-details: always
        # Probe personnalisée pour les dépendances critiques
        critical:
          include: db,paymentGateway
          show-details: when-authorized
  health:
    # Active les états Kubernetes
    livenessstate:
      enabled: true
    readinessstate:
      enabled: true
KubernetesHealthConfig.javajava
// Configuration programmatique des groupes
package com.example.monitoring.config;

import org.springframework.boot.actuate.availability.LivenessStateHealthIndicator;
import org.springframework.boot.actuate.availability.ReadinessStateHealthIndicator;
import org.springframework.boot.availability.ApplicationAvailability;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class KubernetesHealthConfig {

    @Bean
    LivenessStateHealthIndicator livenessStateHealthIndicator(
            ApplicationAvailability availability) {
        return new LivenessStateHealthIndicator(availability);
    }

    @Bean
    ReadinessStateHealthIndicator readinessStateHealthIndicator(
            ApplicationAvailability availability) {
        return new ReadinessStateHealthIndicator(availability);
    }
}

Les probes Kubernetes pointent alors vers les endpoints dédiés :

yaml
# kubernetes-deployment.yml
# Configuration des probes Kubernetes
spec:
  containers:
    - name: order-service
      livenessProbe:
        httpGet:
          path: /actuator/health/liveness
          port: 8080
        initialDelaySeconds: 30
        periodSeconds: 10
        failureThreshold: 3
      readinessProbe:
        httpGet:
          path: /actuator/health/readiness
          port: 8080
        initialDelaySeconds: 10
        periodSeconds: 5
        failureThreshold: 3

Intégration Prometheus et Grafana

Configuration du scraping Prometheus

Prometheus collecte les métriques en interrogeant périodiquement l'endpoint /actuator/prometheus. La configuration définit les cibles à scraper.

yaml
# prometheus.yml
# Configuration Prometheus pour Spring Boot
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'spring-boot-apps'
    metrics_path: '/actuator/prometheus'
    scrape_interval: 10s
    static_configs:
      - targets:
          - 'order-service:8080'
          - 'payment-service:8080'
          - 'inventory-service:8080'
    # Relabeling pour ajouter des métadonnées
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance
        regex: '([^:]+):\d+'
        replacement: '${1}'

  # Service discovery Kubernetes
  - job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      # Scrape uniquement les pods avec l'annotation
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
        action: replace
        target_label: __metrics_path__
        regex: (.+)

Métriques JVM exposées par défaut

Actuator avec Micrometer expose automatiquement des métriques JVM détaillées. Voici les plus importantes pour le monitoring.

promql
# Requêtes PromQL pour le monitoring JVM

# Utilisation mémoire heap
jvm_memory_used_bytes{area="heap"}

# Pourcentage mémoire utilisée
jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"} * 100

# Threads actifs
jvm_threads_live_threads

# Garbage collection - temps passé
rate(jvm_gc_pause_seconds_sum[5m])

# Nombre de GC par minute
rate(jvm_gc_pause_seconds_count[1m]) * 60

# CPU utilisé par la JVM
process_cpu_usage

# Connexions base de données actives
hikaricp_connections_active

# Pool de connexions - utilisation
hikaricp_connections_active / hikaricp_connections_max * 100
CustomJvmMetrics.javajava
// Métriques JVM additionnelles
package com.example.monitoring.metrics;

import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.MeterBinder;
import org.springframework.stereotype.Component;

import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;

@Component
public class CustomJvmMetrics implements MeterBinder {

    @Override
    public void bindTo(MeterRegistry registry) {
        OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();

        // Load average système
        Gauge.builder("system.load.average", osBean, OperatingSystemMXBean::getSystemLoadAverage)
            .description("System load average over 1 minute")
            .register(registry);

        // Nombre de processeurs disponibles
        Gauge.builder("system.cpu.count", osBean, OperatingSystemMXBean::getAvailableProcessors)
            .description("Number of available processors")
            .register(registry);

        // Uptime de l'application
        Gauge.builder("application.uptime.seconds",
                ManagementFactory.getRuntimeMXBean(),
                bean -> bean.getUptime() / 1000.0)
            .description("Application uptime in seconds")
            .register(registry);
    }
}

Dashboard Grafana prêt à l'emploi

Grafana propose des dashboards préconfigurés pour Spring Boot. Le dashboard ID 12900 offre une vue complète des métriques Actuator.

json
{
  "annotations": {
    "list": []
  },
  "panels": [
    {
      "title": "Request Rate",
      "type": "graph",
      "targets": [
        {
          "expr": "rate(http_server_requests_seconds_count{application=\"$application\"}[5m])",
          "legendFormat": "{{method}} {{uri}} - {{status}}"
        }
      ]
    },
    {
      "title": "Response Time P99",
      "type": "graph",
      "targets": [
        {
          "expr": "histogram_quantile(0.99, rate(http_server_requests_seconds_bucket{application=\"$application\"}[5m]))",
          "legendFormat": "{{method}} {{uri}}"
        }
      ]
    },
    {
      "title": "Error Rate",
      "type": "singlestat",
      "targets": [
        {
          "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\",status=~\"5..\"}[5m])) / sum(rate(http_server_requests_seconds_count{application=\"$application\"}[5m])) * 100"
        }
      ]
    }
  ]
}
Import Grafana

Pour importer un dashboard : Grafana → Dashboards → Import → ID 12900 (Spring Boot Statistics) ou 4701 (JVM Micrometer). Ces dashboards fonctionnent directement avec les métriques Actuator standard.

Alerting avec Prometheus

Règles d'alerte essentielles

Les règles d'alerte Prometheus déclenchent des notifications lorsque les métriques dépassent des seuils critiques.

yaml
# alerting-rules.yml
# Règles d'alerte pour applications Spring Boot
groups:
  - name: spring-boot-alerts
    rules:
      # Alerte si l'application est down
      - alert: ApplicationDown
        expr: up{job="spring-boot-apps"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Application {{ $labels.instance }} is down"
          description: "{{ $labels.instance }} has been down for more than 1 minute"

      # Alerte sur le taux d'erreurs HTTP
      - alert: HighErrorRate
        expr: |
          sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m])) by (application)
          /
          sum(rate(http_server_requests_seconds_count[5m])) by (application)
          > 0.05
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High error rate on {{ $labels.application }}"
          description: "Error rate is {{ $value | humanizePercentage }}"

      # Alerte sur la latence P99
      - alert: HighLatency
        expr: |
          histogram_quantile(0.99,
            rate(http_server_requests_seconds_bucket[5m])
          ) > 2
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High latency detected"
          description: "P99 latency is {{ $value | humanizeDuration }}"

      # Alerte mémoire heap
      - alert: HighHeapUsage
        expr: |
          jvm_memory_used_bytes{area="heap"}
          / jvm_memory_max_bytes{area="heap"}
          > 0.85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High heap memory usage on {{ $labels.instance }}"
          description: "Heap usage is at {{ $value | humanizePercentage }}"

      # Alerte connexions base de données épuisées
      - alert: DatabaseConnectionPoolExhausted
        expr: |
          hikaricp_connections_active
          / hikaricp_connections_max
          > 0.9
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Database connection pool nearly exhausted"
          description: "{{ $value | humanizePercentage }} of connections in use"

      # Alerte GC excessif
      - alert: HighGCPause
        expr: |
          rate(jvm_gc_pause_seconds_sum[5m])
          / rate(jvm_gc_pause_seconds_count[5m])
          > 0.5
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High GC pause time"
          description: "Average GC pause is {{ $value | humanizeDuration }}"

Ces alertes couvrent les problèmes les plus courants en production : disponibilité, performance, et ressources.

Métriques HTTP et base de données

Instrumentation automatique des requêtes HTTP

Spring Boot 3 instrumente automatiquement toutes les requêtes HTTP entrantes avec des métriques détaillées.

yaml
# application.yml
# Configuration des métriques HTTP
management:
  metrics:
    distribution:
      # Active les histogrammes pour les percentiles
      percentiles-histogram:
        http.server.requests: true
      percentiles:
        http.server.requests: 0.5, 0.75, 0.95, 0.99
      # Définit les buckets SLA
      slo:
        http.server.requests: 100ms, 500ms, 1s, 2s
    tags:
      # Tags globaux ajoutés à toutes les métriques
      application: ${spring.application.name}
      environment: ${spring.profiles.active:default}
WebMvcMetricsConfig.javajava
// Personnalisation des tags HTTP
package com.example.monitoring.config;

import io.micrometer.core.instrument.Tag;
import org.springframework.boot.actuate.metrics.web.servlet.WebMvcTagsContributor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerMapping;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Collections;

@Configuration
public class WebMvcMetricsConfig {

    @Bean
    WebMvcTagsContributor customTagsContributor() {
        return (request, response, handler, exception) -> {
            // Ajoute des tags personnalisés aux métriques HTTP
            String userId = request.getHeader("X-User-Id");
            String tenantId = request.getHeader("X-Tenant-Id");

            return java.util.List.of(
                Tag.of("user.type", userId != null ? "authenticated" : "anonymous"),
                Tag.of("tenant", tenantId != null ? tenantId : "default")
            );
        };
    }
}

Métriques HikariCP et requêtes SQL

Les métriques du pool de connexions HikariCP sont exposées automatiquement. Pour les requêtes SQL, une configuration supplémentaire permet de tracer la durée des queries.

yaml
# application.yml
# Configuration HikariCP avec métriques
spring:
  datasource:
    hikari:
      pool-name: OrderServicePool
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000
      # Active les métriques détaillées
      register-mbeans: true
DataSourceMetricsConfig.javajava
// Métriques supplémentaires pour les requêtes SQL
package com.example.monitoring.config;

import io.micrometer.core.instrument.MeterRegistry;
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;

@Configuration
public class DataSourceMetricsConfig {

    @Bean
    @Primary
    DataSource metricsDataSource(
            DataSourceProperties properties,
            MeterRegistry registry) {

        // DataSource original
        DataSource originalDataSource = properties
            .initializeDataSourceBuilder()
            .build();

        // Proxy avec métriques
        return ProxyDataSourceBuilder.create(originalDataSource)
            .name("order-service-db")
            .listener(new MicrometerQueryMetricsListener(registry))
            .logQueryBySlf4j(SLF4JLogLevel.DEBUG)
            .build();
    }
}
MicrometerQueryMetricsListener.javajava
// Listener pour les métriques de requêtes SQL
package com.example.monitoring.metrics;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import net.ttddyy.dsproxy.ExecutionInfo;
import net.ttddyy.dsproxy.QueryInfo;
import net.ttddyy.dsproxy.listener.QueryExecutionListener;

import java.util.List;
import java.util.concurrent.TimeUnit;

public class MicrometerQueryMetricsListener implements QueryExecutionListener {

    private final Timer queryTimer;

    public MicrometerQueryMetricsListener(MeterRegistry registry) {
        this.queryTimer = Timer.builder("sql.query.duration")
            .description("Durée d'exécution des requêtes SQL")
            .publishPercentiles(0.5, 0.95, 0.99)
            .register(registry);
    }

    @Override
    public void beforeQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList) {
        // Avant l'exécution
    }

    @Override
    public void afterQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList) {
        // Enregistre la durée de chaque requête
        long elapsedTime = execInfo.getElapsedTime();
        queryTimer.record(elapsedTime, TimeUnit.MILLISECONDS);
    }
}

Bonnes pratiques pour la production

Cardinalité des métriques

Une cardinalité excessive dégrade les performances de Prometheus. Chaque combinaison unique de tags crée une série temporelle distincte.

AntiPatternHighCardinality.javajava
// ❌ À ÉVITER - Cardinalité explosive
package com.example.monitoring.antipattern;

@Service
public class AntiPatternHighCardinality {

    private final MeterRegistry registry;

    // ❌ MAUVAIS : userId crée une série par utilisateur
    public void trackUserAction(String userId, String action) {
        Counter.builder("user.actions")
            .tag("userId", userId)  // Millions de valeurs possibles !
            .tag("action", action)
            .register(registry)
            .increment();
    }
}
GoodPracticeCardinality.javajava
// ✅ Cardinalité contrôlée
package com.example.monitoring.bestpractice;

@Service
public class GoodPracticeCardinality {

    private final MeterRegistry registry;

    // ✅ BON : Catégorie utilisateur au lieu d'ID
    public void trackUserAction(User user, String action) {
        Counter.builder("user.actions")
            .tag("userType", user.getSubscriptionType())  // FREE, PREMIUM, ENTERPRISE
            .tag("action", action)
            .register(registry)
            .increment();
    }

    // ✅ BON : Regroupement par plage
    public void trackResponseTime(long responseTimeMs) {
        String bucket = categorizeResponseTime(responseTimeMs);
        Counter.builder("response.time.bucket")
            .tag("bucket", bucket)  // fast, normal, slow, very_slow
            .register(registry)
            .increment();
    }

    private String categorizeResponseTime(long ms) {
        if (ms < 100) return "fast";
        if (ms < 500) return "normal";
        if (ms < 2000) return "slow";
        return "very_slow";
    }
}

Configuration production-ready

yaml
# application-production.yml
# Configuration optimisée pour la production
management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus
  endpoint:
    health:
      show-details: when-authorized
      probes:
        enabled: true
  metrics:
    export:
      prometheus:
        enabled: true
        step: 30s
    distribution:
      percentiles-histogram:
        http.server.requests: true
      minimum-expected-value:
        http.server.requests: 1ms
      maximum-expected-value:
        http.server.requests: 30s
    tags:
      application: ${spring.application.name}
      environment: production
      version: ${app.version:unknown}
  server:
    # Port séparé pour les endpoints de management
    port: 9090

# Désactive les endpoints non essentiels en production
  endpoint:
    env:
      enabled: false
    beans:
      enabled: false
    configprops:
      enabled: false
    mappings:
      enabled: false

Conclusion

Spring Boot Actuator combiné avec Micrometer et Prometheus offre une solution de monitoring complète :

Configuration minimale : endpoints prêts à l'emploi avec Spring Boot Starter

Métriques JVM automatiques : mémoire, threads, GC, CPU sans code additionnel

Métriques personnalisées : Counter, Gauge, Timer avec annotations @Timed/@Counted

Health Indicators : vérification des dépendances externes et états Kubernetes

Intégration Prometheus : format standard pour le scraping et l'alerting

Sécurité intégrée : contrôle d'accès aux endpoints sensibles

Dashboards Grafana : visualisation immédiate avec dashboards préconfigurés

Alerting : règles PromQL pour détecter les anomalies en production

Cette stack d'observabilité forme la base indispensable pour opérer des applications Spring Boot en production avec confiance.

Passe à la pratique !

Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.

Tags

#spring boot actuator
#micrometer
#prometheus
#monitoring
#observabilité

Partager

Articles similaires