Cette page parcourt les patrons d’instrumentation MP Metrics 5.1.1 couramment rencontrés. Tous les exemples reposent strictement sur les annotations standard org.eclipse.microprofile.metrics.annotation.* ; aucun import propriétaire Dirac n’est jamais requis dans le code applicatif.
Coordonnées Maven
Seule la spec MP Metrics est compilée par l’application :
<dependency>
<groupId>org.eclipse.microprofile.metrics</groupId>
<artifactId>microprofile-metrics-api</artifactId>
<version>5.1.1</version>
<scope>provided</scope>
</dependency>
Ajoutez dirac-cdi-vauban pour les intercepteurs et la BCE, et dirac-rest pour l’endpoint GET /metrics.
Compteur — @Counted
Compteur monotone, incrémenté à chaque invocation. Implémentation interne : LongAdder — sans contention sur la concurrence forte.
@ApplicationScoped
public class OrderService {
@Counted(name = "orders_placed_total",
description = "Total number of orders placed",
tags = "endpoint=orders")
public void placeOrder(String id) {
// ...
}
}
absolute=true (par défaut false) supprime le préfixage par le nom de la classe.
Minuterie — @Timed
Mesure de durée par appel, agrégée dans un HistogramImpl. Mesure via System.nanoTime() autour de proceed().
@ApplicationScoped
public class CheckoutService {
@Timed(name = "order_checkout_seconds",
description = "Duration of checkout flow",
unit = MetricUnits.SECONDS)
public Receipt checkout(Cart cart) {
// ...
}
}
unit est libre (le formatter ne fait pas de conversion automatique) — par convention, exprimer la durée en MetricUnits.SECONDS pour Prometheus.
Jauge — @Gauge
Valeur instantanée renvoyée par une méthode du bean. Résolution par MethodHandle au démarrage par DiracExtension — jamais par java.lang.reflect.Proxy.
@ApplicationScoped
public class QueueService {
private final BlockingQueue<Order> backlog = new LinkedBlockingQueue<>();
@Gauge(name = "backlog_size",
unit = "none",
description = "Pending orders in backlog")
public int backlogSize() {
return backlog.size();
}
}
|
La méthode jauge doit retourner un type numérique ( |
Métriques programmatiques
Pour les cas qui ne s’expriment pas via annotation (compteur incrémenté depuis un callback non interceptable, histogramme alimenté en lot, etc.) :
@ApplicationScoped
public class RateLimiter {
@Inject
MetricRegistry registry;
private Counter rejections;
@PostConstruct
void init() {
rejections = registry.counter(
Metadata.builder()
.withName("rate_limited_total")
.withDescription("Requests rejected by rate limit")
.build(),
new Tag("policy", "burst"));
}
public void reject() {
rejections.inc();
}
}
MetricRegistry.counter/histogram/timer/gauge(…) est idempotent sur MetricID : un second appel renvoie l’instance déjà enregistrée.
Tags d’annotation
Le format spec est tags = { "clef1=valeur1", "clef2=valeur2" }. L’unicité d’une métrique se calcule sur l’union (name, tags d’annotation, tags globaux mp.metrics.tags).
@Counted(name = "http_requests_total",
tags = { "method=GET", "endpoint=orders" })
public List<Order> list() {
// ...
}
Pour des tags dynamiques (par requête, par utilisateur), utiliser l’API programmatique :
registry.counter("http_requests_total",
new Tag("method", request.method()),
new Tag("status", String.valueOf(response.status())))
.inc();
Percentiles et buckets — distribution
La spec §4 expose trois clés mp.metrics.distribution.* lues via DistributionConfig. Elles s’appliquent globalement ou par métrique nommée.
# microprofile-config.properties
mp.metrics.distribution.percentiles=0.5,0.75,0.95,0.99
mp.metrics.distribution.percentiles=order_checkout_seconds=0.5,0.95,0.99
mp.metrics.distribution.timer.buckets=order_checkout_seconds=10ms,50ms,250ms,1s,5s
Une valeur vide (mp.metrics.distribution.percentiles=) désactive les percentiles globalement et n’émet plus que _count et _sum.
Tags globaux d’application
mp.metrics.tags=app=orders,env=prod ajoute deux tags à toutes les métriques, à toutes les expositions, sans modifier le code applicatif.
mp.metrics.appName=orders-service
mp.metrics.tags=app=orders-service,env=prod,region=eu-west-3
Ces clés sont résolues via Ravel qui implémente MicroProfile Config 3.1.
Exposition HTTP
Avec dirac-rest sur le classpath, l’endpoint suit la spec MP Metrics §3 :
# OpenMetrics / Prometheus text (défaut)
curl http://localhost:8080/metrics
# JSON (MP Metrics §3.2)
curl -H 'Accept: application/json' http://localhost:8080/metrics
# Un seul scope
curl http://localhost:8080/metrics/application
curl http://localhost:8080/metrics/base
curl http://localhost:8080/metrics/vendor
# Une famille précise
curl http://localhost:8080/metrics/application/orders_placed_total
La négociation se fait sur Accept. La sortie OpenMetrics ressemble à :
# HELP order_checkout_seconds Duration of checkout flow
# TYPE order_checkout_seconds summary
order_checkout_seconds{mp_scope="application",quantile="0.5"} 0.024
order_checkout_seconds{mp_scope="application",quantile="0.95"} 0.072
order_checkout_seconds_count{mp_scope="application"} 1542
order_checkout_seconds_sum{mp_scope="application"} 38.71
Métriques JVM (BASE)
Pas de code à écrire : BaseMetricsRegistrar peuple le registre BASE au démarrage, via DiracExtension. Les métriques listées par la spec §5 (mémoire, GC, threads, classes, CPU, uptime) sont exposées automatiquement sur GET /metrics/base.
curl http://localhost:8080/metrics/base
Pour aller plus loin
-
Référence — annotations exhaustives, clés
mp.metrics.*, typesMetricUnits. -
Concepts — modèle, scopes, tags.
-
Fonctionnement interne — implémentation lock-free, BCE.