Intro aux métriques Dropwizard

1. Introduction

Metrics est une bibliothèque Java qui fournit des instruments de mesure pour les applications Java.

Il comporte plusieurs modules. Dans cet article, nous élaborerons les modules metrics-core, metrics-healthchecks, metrics-servlets et metrics-servlet, ainsi que les autres éléments, pour votre référence.

2. Module metrics-core

2.1. Dépendances Maven

Pour utiliser le module metrics-core , une seule dépendance doit être ajoutée au fichier pom.xml :

<dependency>
    <groupId>io.dropwizard.metrics</groupId>
    <artifactId>metrics-core</artifactId>
    <version>3.1.2</version>
</dependency>

Et vous pouvez trouver sa dernière version ici .

2.2. MetricRegistry

En termes simples, nous allons utiliser la classe MetricRegistry pour enregistrer une ou plusieurs métriques.

Nous pouvons utiliser un seul registre de métriques pour toutes nos métriques, mais si nous voulons utiliser différentes méthodes de reporting pour différentes métriques, nous pouvons également diviser nos métriques en groupes et utiliser des registres de métriques différents pour chaque groupe.

Créons un MetricRegistry maintenant:

MetricRegistry metricRegistry = new MetricRegistry();

Et puis nous pouvons enregistrer des métriques avec ce MetricRegistry :

Meter meter1 = new Meter();
metricRegistry.register("meter1", meter1);

Meter meter2 = metricRegistry.meter("meter2");

Il existe deux méthodes de base pour créer une nouvelle métrique: en créer une vous-même ou en obtenir une à partir du registre de métriques. Comme vous pouvez le constater, nous avons utilisé les deux dans l’exemple ci-dessus. Nous instancions l’objet Meter «mètre1» et nous obtenons un autre objet Meter «mètre2» créé par metricRegistry .

Dans un registre de métriques, chaque métrique a un nom unique, car nous avons utilisé «mètre1» et «mètre2» comme noms de métriques ci-dessus. MetricRegistry fournit également un ensemble de méthodes d’assistance statique pour nous aider à créer les noms de métriques appropriés:

String name1 = MetricRegistry.name(Filter.class, "request", "count");
String name2 = MetricRegistry.name("CustomFilter", "response", "count");

Si nous devons gérer un ensemble de registres de métriques, nous pouvons utiliser la classe SharedMetricRegistries , qui est un singleton et un thread-safe. Nous pouvons ajouter un registre de métrique à celui-ci, récupérer ce registre de métrique et le supprimer:

SharedMetricRegistries.add("default", metricRegistry);
MetricRegistry retrievedMetricRegistry = SharedMetricRegistries.getOrCreate("default");
SharedMetricRegistries.remove("default");

** 3. Concepts métriques

**

Le module metrics-core fournit plusieurs types de métriques couramment utilisés:

Meter , Gauge , Counter , Histogram et Timer et Reporter pour afficher les valeurs des métriques

3.1. Mètre

Un compteur mesure le nombre d’occurrences et son taux

Meter meter = new Meter();
long initCount = meter.getCount();
assertThat(initCount, equalTo(0L));

meter.mark();
assertThat(meter.getCount(), equalTo(1L));

meter.mark(20);
assertThat(meter.getCount(), equalTo(21L));

double meanRate = meter.getMeanRate();
double oneMinRate = meter.getOneMinuteRate();
double fiveMinRate = meter.getFiveMinuteRate();
double fifteenMinRate = meter.getFifteenMinuteRate();

La méthode getCount () renvoie le nombre d’occurrences d’événements et la méthode mark () ajoute 1 ou n au nombre d’occurrences d’événements. L’objet Meter fournit quatre débits représentant les débits moyens pour toute la durée de vie de Meter , pour la minute récente, les cinq dernières minutes et le trimestre récent, respectivement.

3.2. Jauge

Gauge est une interface qui sert simplement à renvoyer une valeur particulière. Le module metrics-core en fournit plusieurs implémentations:

RatioGauge , CachedGauge , DerivativeGauge et http://metrics.dropwizard.io/3.1 .0/apidocs/com/codahale/metrics/JmxAttributeGauge.html[ JmxAttributeGauge ].

RatioGauge est une classe abstraite et mesure le rapport d’une valeur à une autre.

Voyons comment l’utiliser. Tout d’abord, nous implémentons une classe AttendanceRatioGauge :

public class AttendanceRatioGauge extends RatioGauge {
    private int attendanceCount;
    private int courseCount;

    @Override
    protected Ratio getRatio() {
        return Ratio.of(attendanceCount, courseCount);
    }

   //standard constructors
}

Et puis on le teste:

RatioGauge ratioGauge = new AttendanceRatioGauge(15, 20);

assertThat(ratioGauge.getValue(), equalTo(0.75));

CachedGauge est une autre classe abstraite pouvant mettre en cache une valeur. Par conséquent, elle est très utile lorsque le calcul des valeurs est coûteux. Pour l’utiliser, nous devons implémenter une classe ActiveUsersGauge :

public class ActiveUsersGauge extends CachedGauge<List<Long>> {

    @Override
    protected List<Long> loadValue() {
        return getActiveUserCount();
    }

    private List<Long> getActiveUserCount() {
        List<Long> result = new ArrayList<Long>();
        result.add(12L);
        return result;
    }

   //standard constructors
}

Ensuite, nous le testons pour voir s’il fonctionne comme prévu:

Gauge<List<Long>> activeUsersGauge = new ActiveUsersGauge(15, TimeUnit.MINUTES);
List<Long> expected = new ArrayList<>();
expected.add(12L);

assertThat(activeUsersGauge.getValue(), equalTo(expected));

Nous avons défini le délai d’expiration du cache sur 15 minutes lors de l’instanciation de ActiveUsersGauge .

DerivativeGauge est également une classe abstraite et vous permet de dériver une valeur à partir d’un autre Gauge .

Regardons un exemple:

public class ActiveUserCountGauge extends DerivativeGauge<List<Long>, Integer> {

    @Override
    protected Integer transform(List<Long> value) {
        return value.size();
    }

   //standard constructors
}

Ce Gauge tire sa valeur d’un ActiveUsersGauge . Nous nous attendons donc à ce que ce soit la valeur de la taille de la liste de base:

Gauge<List<Long>> activeUsersGauge = new ActiveUsersGauge(15, TimeUnit.MINUTES);
Gauge<Integer> activeUserCountGauge = new ActiveUserCountGauge(activeUsersGauge);

assertThat(activeUserCountGauge.getValue(), equalTo(1));

JmxAttributeGauge est utilisé lorsque nous devons accéder aux métriques des autres bibliothèques exposées via JMX.

3.3. Compteur

Le Compteur est utilisé pour enregistrer des incrémentations et des décrémentations:

Counter counter = new Counter();
long initCount = counter.getCount();
assertThat(initCount, equalTo(0L));

counter.inc();
assertThat(counter.getCount(), equalTo(1L));

counter.inc(11);
assertThat(counter.getCount(), equalTo(12L));

counter.dec();
assertThat(counter.getCount(), equalTo(11L));

counter.dec(6);
assertThat(counter.getCount(), equalTo(5L));

3.4. Histogramme

Histogram est utilisé pour suivre un flux de valeurs Long et il analyse leurs caractéristiques statistiques telles que max, min, moyenne, médiane, écart type, 75e centile , etc.:

Histogram histogram = new Histogram(new UniformReservoir());
histogram.update(5);
long count1 = histogram.getCount();
assertThat(count1, equalTo(1L));

Snapshot snapshot1 = histogram.getSnapshot();
assertThat(snapshot1.getValues().length, equalTo(1));
assertThat(snapshot1.getValues()[0], equalTo(5L));

histogram.update(20);
long count2 = histogram.getCount();
assertThat(count2, equalTo(2L));

Snapshot snapshot2 = histogram.getSnapshot();
assertThat(snapshot2.getValues().length, equalTo(2));
assertThat(snapshot2.getValues()[1], equalTo(20L));
assertThat(snapshot2.getMax(), equalTo(20L));
assertThat(snapshot2.getMean(), equalTo(12.5));
assertEquals(10.6, snapshot2.getStdDev(), 0.1);
assertThat(snapshot2.get75thPercentile(), equalTo(20.0));
assertThat(snapshot2.get999thPercentile(), equalTo(20.0));

Histogram échantillonne les données en utilisant l’échantillonnage de réservoir. Lorsque nous instancions un objet Histogram , nous devons définir explicitement son réservoir.

Reservoir est une interface et metrics-core en fournit quatre implémentations:

ExponentiallyDecayingReservoir , UniformReservoir , SlidingTimeWindowReservoir , httdp://metricdhpwg/apidocs/com/codahale/metrics/SlidingWindowReservoir.html[SlidingWindowReservoir].

Dans la section ci-dessus, nous avons mentionné qu’une métrique peut également être créée par MetricRegistry, en plus d’utiliser un constructeur _. Lorsque nous utilisons metricRegistry.histogram () , elle renvoie une instance de Histogram avec ExponentiallyDecayingReservoir_ .

3.5. Minuteur

Timer est utilisé pour garder trace de plusieurs durées de temporisation représentées par des objets Context et fournit également leurs données statistiques:

Timer timer = new Timer();
Timer.Context context1 = timer.time();
TimeUnit.SECONDS.sleep(5);
long elapsed1 = context1.stop();

assertEquals(5000000000L, elapsed1, 1000000);
assertThat(timer.getCount(), equalTo(1L));
assertEquals(0.2, timer.getMeanRate(), 0.1);

Timer.Context context2 = timer.time();
TimeUnit.SECONDS.sleep(2);
context2.close();

assertThat(timer.getCount(), equalTo(2L));
assertEquals(0.3, timer.getMeanRate(), 0.1);

3.6. Journaliste

Lorsque nous devons sortir nos mesures, nous pouvons utiliser Reporter . Il s’agit d’une interface et le module metrics-core fournit plusieurs implémentations, telles que ConsoleReporter , CsvReporter , http://metrics.dropwizard.io/3.1.0/apidocs/com/codahale/metrics/Slf4jReporter .html[ Slf4jReporter ], JmxReporter , etc.

Ici, nous utilisons ConsoleReporter comme exemple:

MetricRegistry metricRegistry = new MetricRegistry();

Meter meter = metricRegistry.meter("meter");
meter.mark();
meter.mark(200);
Histogram histogram = metricRegistry.histogram("histogram");
histogram.update(12);
histogram.update(17);
Counter counter = metricRegistry.counter("counter");
counter.inc();
counter.dec();

ConsoleReporter reporter = ConsoleReporter.forRegistry(metricRegistry).build();
reporter.start(5, TimeUnit.MICROSECONDS);
reporter.report();

Voici l’exemple de sortie de ConsoleReporter:

-- Histograms ------------------------------------------------------------------
histogram
count = 2
min = 12
max = 17
mean = 14.50
stddev = 2.50
median = 17.00
75% <= 17.00
95% <= 17.00
98% <= 17.00
99% <= 17.00
99.9% <= 17.00

-- Meters ----------------------------------------------------------------------
meter
count = 201
mean rate = 1756.87 events/second
1-minute rate = 0.00 events/second
5-minute rate = 0.00 events/second
15-minute rate = 0.00 events/second

4. Module metrics-healthchecks

Métriques comporte un module d’extension metrics-healthchecks permettant de gérer les contrôles d’intégrité.

4.1. Dépendances Maven

Pour utiliser le module metrics-healthchecks, nous devons ajouter cette dépendance au fichier pom.xml :

<dependency>
    <groupId>io.dropwizard.metrics</groupId>
    <artifactId>metrics-healthchecks</artifactId>
    <version>3.1.2</version>
</dependency>

Et vous pouvez trouver sa dernière version ici .

4.2. Usage

Premièrement, nous avons besoin de plusieurs classes responsables d’opérations spécifiques d’état de santé et ces classes doivent implémenter HealthCheck .

Par exemple, nous utilisons DatabaseHealthCheck et UserCenterHealthCheck :

public class DatabaseHealthCheck extends HealthCheck {

    @Override
    protected Result check() throws Exception {
        return Result.healthy();
    }
}
public class UserCenterHealthCheck extends HealthCheck {

    @Override
    protected Result check() throws Exception {
        return Result.healthy();
    }
}

Ensuite, nous avons besoin d’un HealthCheckRegistry (qui est semblable à MetricRegistry ) et enregistrer le DatabaseHealthCheck et UserCenterHealthCheck avec:

HealthCheckRegistry healthCheckRegistry = new HealthCheckRegistry();
healthCheckRegistry.register("db", new DatabaseHealthCheck());
healthCheckRegistry.register("uc", new UserCenterHealthCheck());

assertThat(healthCheckRegistry.getNames().size(), equalTo(2));

Nous pouvons également annuler l’enregistrement du HealthCheck :

healthCheckRegistry.unregister("uc");

assertThat(healthCheckRegistry.getNames().size(), equalTo(1));

Nous pouvons exécuter toutes les instances de HealthCheck :

Map<String, HealthCheck.Result> results = healthCheckRegistry.runHealthChecks();
for (Map.Entry<String, HealthCheck.Result> entry : results.entrySet()) {
    assertThat(entry.getValue().isHealthy(), equalTo(true));
}

Enfin, nous pouvons exécuter une instance spécifique de HealthCheck :

healthCheckRegistry.runHealthCheck("db");

5. Module metrics-servlets

Les métriques nous fournissent une poignée de servlets utiles qui nous permettent d’accéder aux données liées aux métriques via des requêtes HTTP.

5.1. Dépendances Maven

Pour utiliser le module metrics-servlets, nous devons ajouter cette dépendance au fichier pom.xml :

<dependency>
    <groupId>io.dropwizard.metrics</groupId>
    <artifactId>metrics-servlets</artifactId>
    <version>3.1.2</version>
</dependency>

Et vous pouvez trouver sa dernière version ici .

5.2. HealthCheckServlet Usage

HealthCheckServlet fournit des résultats de contrôle d’intégrité. Premièrement, nous devons créer un ServletContextListener qui expose notre HealthCheckRegistry :

public class MyHealthCheckServletContextListener
  extends HealthCheckServlet.ContextListener {

    public static HealthCheckRegistry HEALTH__CHECK__REGISTRY
      = new HealthCheckRegistry();

    static {
        HEALTH__CHECK__REGISTRY.register("db", new DatabaseHealthCheck());
    }

    @Override
    protected HealthCheckRegistry getHealthCheckRegistry() {
        return HEALTH__CHECK__REGISTRY;
    }
}

Ensuite, nous ajoutons cet écouteur et HealthCheckServlet dans le fichier web.xml :

<listener>
    <listener-class>com.baeldung.metrics.servlets.MyHealthCheckServletContextListener</listener-class>
</listener>
<servlet>
    <servlet-name>healthCheck</servlet-name>
    <servlet-class>com.codahale.metrics.servlets.HealthCheckServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>healthCheck</servlet-name>
    <url-pattern>/healthcheck</url-pattern>
</servlet-mapping>

Nous pouvons maintenant lancer l’application Web et envoyer une demande GET à «http://localhost: 8080/healthcheck» pour obtenir les résultats de la vérification de l’état. Sa réponse devrait être comme ceci:

{
  "db": {
    "healthy": true
  }
}

5.3. ThreadDumpServlet Usage

ThreadDumpServlet fournit des informations sur tous les threads actifs de la JVM, leurs états, leurs traces de pile et l’état de tous les verrous qu’ils peuvent attendre. Si nous voulons l’utiliser, nous devons simplement les ajouter dans le fichier web.xml :

<servlet>
    <servlet-name>threadDump</servlet-name>
    <servlet-class>com.codahale.metrics.servlets.ThreadDumpServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>threadDump</servlet-name>
    <url-pattern>/threaddump</url-pattern>
</servlet-mapping>

Les données de vidage de threads seront disponibles sur “http://localhost: 8080/threaddump”.

5.4. PingServlet Usage

PingServlet peut être utilisé pour tester si l’application est en cours d’exécution. Nous les ajoutons dans le fichier web.xml :

<servlet>
    <servlet-name>ping</servlet-name>
    <servlet-class>com.codahale.metrics.servlets.PingServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>ping</servlet-name>
    <url-pattern>/ping</url-pattern>
</servlet-mapping>

Puis envoyez une requête GET à “http://localhost: 8080/ping”. Le code d’état de la réponse est 200 et son contenu est «pong».

5.5. MetricsServlet Usage

MetricsServlet fournit des données de métriques. Premièrement, nous devons créer un ServletContextListener qui expose notre MetricRegistry :

public class MyMetricsServletContextListener
  extends MetricsServlet.ContextListener {
    private static MetricRegistry METRIC__REGISTRY
     = new MetricRegistry();

    static {
        Counter counter = METRIC__REGISTRY.counter("m01-counter");
        counter.inc();

        Histogram histogram = METRIC__REGISTRY.histogram("m02-histogram");
        histogram.update(5);
        histogram.update(20);
        histogram.update(100);
    }

    @Override
    protected MetricRegistry getMetricRegistry() {
        return METRIC__REGISTRY;
    }
}

Cet écouteur et MetricsServlet doivent être ajoutés à web.xml :

<listener>
    <listener-class>com.codahale.metrics.servlets.MyMetricsServletContextListener</listener-class>
</listener>
<servlet>
    <servlet-name>metrics</servlet-name>
    <servlet-class>com.codahale.metrics.servlets.MetricsServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>metrics</servlet-name>
    <url-pattern>/metrics</url-pattern>
</servlet-mapping>

Cela sera exposé dans notre application Web à l’adresse «http://localhost: 8080/metrics». Sa réponse devrait contenir diverses données de métriques:

{
  "version": "3.0.0",
  "gauges": {},
  "counters": {
    "m01-counter": {
      "count": 1
    }
  },
  "histograms": {
    "m02-histogram": {
      "count": 3,
      "max": 100,
      "mean": 41.66666666666666,
      "min": 5,
      "p50": 20,
      "p75": 100,
      "p95": 100,
      "p98": 100,
      "p99": 100,
      "p999": 100,
      "stddev": 41.69998667732268
    }
  },
  "meters": {},
  "timers": {}
}

5.6. AdminServlet Usage

AdminServlet regroupe HealthCheckServlet , ThreadDumpServlet , MetricsServlet et PingServlet .

Ajoutons-les au web.xml :

<servlet>
    <servlet-name>admin</servlet-name>
    <servlet-class>com.codahale.metrics.servlets.AdminServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>admin</servlet-name>
    <url-pattern>/admin/** </url-pattern>
</servlet-mapping>

Vous pouvez maintenant y accéder à l’adresse «http://localhost: 8080/admin». Nous aurons une page contenant quatre liens, un pour chacun de ces quatre servlets.

Notez que, si nous souhaitons effectuer un contrôle de l’état et accéder à des données de métriques, ces deux écouteurs sont toujours nécessaires.

6. Module metrics-servlet

Le module metrics-servlet fournit un filtre qui comporte plusieurs mesures: des compteurs pour les codes d’état, un compteur pour le nombre de demandes actives et un minuteur pour la durée de la demande.

6.1. Dépendances Maven

Pour utiliser ce module, ajoutons d’abord la dépendance dans le fichier pom.xml :

<dependency>
    <groupId>io.dropwizard.metrics</groupId>
    <artifactId>metrics-servlet</artifactId>
    <version>3.1.2</version>
</dependency>

Et vous pouvez trouver sa dernière version ici .

6.2. Usage

Pour l’utiliser, nous devons créer un ServletContextListener qui expose notre MetricRegistry au InstrumentedFilter :

public class MyInstrumentedFilterContextListener
  extends InstrumentedFilterContextListener {

    public static MetricRegistry REGISTRY = new MetricRegistry();

    @Override
    protected MetricRegistry getMetricRegistry() {
        return REGISTRY;
    }
}

Ensuite, nous les ajoutons à la web.xml :

<listener>
     <listener-class>
         com.baeldung.metrics.servlet.MyInstrumentedFilterContextListener
     </listener-class>
</listener>

<filter>
    <filter-name>instrumentFilter</filter-name>
    <filter-class>
        com.codahale.metrics.servlet.InstrumentedFilter
    </filter-class>
</filter>
<filter-mapping>
    <filter-name>instrumentFilter</filter-name>
    <url-pattern>/** </url-pattern>
</filter-mapping>

Maintenant, le InstrumentedFilter peut fonctionner. Si nous voulons accéder à ses données de métrique, nous pouvons le faire via son MetricRegistry REGISTRY .

7. Autres modules

À l’exception des modules que nous avons présentés ci-dessus, Metrics propose d’autres modules à des fins différentes:

  • metrics-jvm : fournit plusieurs métriques utiles pour instrumenter la machine virtuelle Java

internes ** metrics-ehcache : fournit InstrumentedEhcache , un décorateur pour

Caches Ehcache ** metrics-httpclient : fournit des classes pour instrumenter Apache

HttpClient (version 4.x) ** metrics-log4j : fournit InstrumentedAppender , un Log4j Appender

implémentation de log4j 1.x qui enregistre le taux d’événements enregistrés par leur niveau de journalisation ** metrics-log4j2 : est similaire à metrics-log4j, juste pour log4j 2.x

  • metrics-logback : fournit InstrumentedAppender , un logback

Implémentation Appender qui enregistre le taux d’événements enregistrés par leur niveau de journalisation ** metrics-json : fournit HealthCheckModule et MetricsModule pour

Jackson

De plus, hormis ces modules de projet principaux, d’autres third party libraries assurent l’intégration avec d’autres bibliothèques et frameworks.

8. Conclusion

L’instrumentation des applications étant une exigence courante, nous avons donc introduit dans cet article Metrics, dans l’espoir qu’il puisse vous aider à résoudre votre problème.

Comme toujours, le code source complet de l’exemple est disponible à l’adresse over sur GitHub .