Introduction
Dans un 1er article, nous avions montré comment monitorer une application Spring Boot, avec Spring Boot Actuator, Prometheus et Grafana.
La solution présentée mettait à notre disposition des métriques prédéfinies, principalement des indicateurs de la JVM (mémoire utilisée, cpu utilisée, pool de connexions, ...).
Ces indicateurs permettent de suivre le fonctionnement technique de notre application mais ne donnent pas d’information sur les problèmes liés au fonctionnel de notre application, comme par exemple le nombre et le type d’erreur survenues lors d’une validation de Captcha Google lors de la soumission un formulaire, ou le nombre et le type d’erreur survenues lors de la recherche d’un PDL (point de livraison) ENEDIS.
L’idée est de disposer d’une unique stack de monitoring (Prometheus/Grafana) pour tous les composants de notre application :
- le socle technique (docker, kubernetes, ….)
- la couche données (SGBD, MOM, …)
- l’API REST qui peut être composées de plusieurs micro services
- les Batchs
- …
Nous pourrons alors disposer d’une vue complète de notre application dans Grafana
Dans cet article, nous allons donc montrer comment créer nos propres métriques, pour monitorer les Exceptions qui surviennent lors de l’utilisation de nos applications Spring Boot, que ce soit une API REST ou un Batch.
Créer nos propres métriques
Pour pouvoir définir nos propres métriques, il faut utiliser la librairie Micrometer.io. On ajoute sa dépendance dans le pom.xml de l’application :
La librairie Micrometer.io permet de définir plusieurs types de métriques :
- les Compteurs (Counter) : ce sont des métriques simples qui servent à compter . On peut seulement incrémenter la valeur du compteur d’un nombre strictement positif.
- les Jauges (Gauge) : ce sont des métriques simples, qui servent à stocker une valeur qui peut évoluer dans le temps, comme par exemple la taille d’une collection, …
- les Timers (Timer & LongTaskTimer) : permettent de mesurer la latence ou la fréquence d'événements.
- les Distributions (DistributionSummary) : permettent de suivre la distribution d’événements, en fonction d’un facteur non temporel. Comme par exemple la répartition de la taille des requêtes à un système en fonction du client.
Spring Boot Actuator définit un registre des métriques (MeterRegistry) qui centralise toutes les métriques.
Par exemple, créer un compteur spécifique est possible via un Builder dédié :
Récupérer un compteur existant revient à requêter la MeterRegistry :
Incrémenter un compteur devient donc aussi simple que ceci :
Pour monitorer les exceptions, nous avons choisi d’utiliser un compteur.
En effet, il nous a semblé plus simple de remonter une valeur strictement croissante dans Prometheus. On obtient donc une valeur brute et on pourra adapter le traitement de cette métrique au cas par cas.
Par exemple, pour une Exception technique signalant un problème de connexion à la base de données, on va pouvoir créer une règle d’alerte si l’augmentation de la valeur du compteur est supérieure à un seuil sur un intervalle d’une minute.
Alors que le nombre important d’exception indiquant une recherche de PDL ENEDIS invalide, dénote plus probablement un problème d’UX au niveau du tunnel de souscription, et ne nécessite pas la mise en place d’une règle d’alerte, mais sera suivie sur un tableau de bord métier.
Simplifier la création d’un compteur via une Annotation
Pour configurer le compteur, nous allons définir une annotation qui permet de définir son nom, son unité et sa description :
Par exemple pour définir le compteur pour une exception qui est levée si le service de validation des captchas est inaccessible, on annotera la classe comme ceci :
Pour créer le compteur défini par l’annotation @Count, on vérifie d’abord que le compteur n’existe pas déjà dans la registry, puis on le crée avec les informations de l’annotation :
Adapter le système pour compter les Exception dans une Application Spring Boot
Pour cela, nous avons défini 3 types d’exceptions :
1. Générique
Avec l’annotation associée :
Et l’exception :
2. Métier
Avec l’annotation associée :
Et l’exception :
3. Technique
Avec l’annotation associée :
Et l’exception :
Et l’énumération correspondante :
Et la classe utilitaire pour gérer les compteurs d’exceptions :
Maintenant que nous disposons d’un outil permettant de compter les exceptions, nous allons voir comment l’utiliser dans une application Spring Boot Web (une API REST) puis dans un Batch Spring Boot.
Compter les Exception dans une Application (API REST) Spring Boot Web
Intercepter les exceptions dans une Application Spring Boot Web
Le mécanisme standard pour intercepter les exceptions dans une application Web Spring Boot repose sur les Exceptionhandlers, définis par l’annotation, qui indique quelles exceptions sont interceptées. Pour intercepter toutes les exceptions, on doit ajouter l’annotation :
Nous allons donc définir notre propre ExceptionHandler, qui devra intercepter toutes les exceptions, puis pour celles annotées par @Count, incrémenter le compteur associé à l’exception, en utilisant la classe utilitaire ExceptionsCounter précédemment décrite.
Comme notre ExceptionHandler doit intercepter tous les types d'exceptions, nous allons en profiter pour standardiser la réponse renvoyée pour chaque exception via la classe ApiError.
En outre, l’annotation Spring Boot @ResponseStatus, nous permet de standardiser la gestion des status HTTP retournés pour chaque Exception (c.f. la méthode getHttpStatus).
Rechercher les métriques dans Prometheus
La règle de nommage d’un compteur dans Prometheus est la suivante :
Si on reprend l’exemple de définition d’un compteur d’exception précédent (c.f.: Simplifier la création d’un compteur via une Annotation) :
Dans Prometheus, cela créera une métrique nommée :
Il suffit alors de saisir le nom de la métrique cherchée dans l’onglet Graph de l’interface Prometheus
Compter les Exceptions dans un Batch Spring Boot
Intercepter les exceptions dans un Batch Spring Boot
Contrairement à une application Web, un Batch Spring Boot, a une exécution séquentielle modélisée par un automate a état fini composé de Step, dans lequel il existe plusieurs moyens pour traiter les exceptions survenues :
- dans un StepExecutionListener, pour intercepter les exceptions survenues au niveau d’un Step
- ou dans un JobExecutionListener, pour intercepter toutes les exceptions au niveau du batch lui-même.
La problématique que nous avons rencontrée était de pouvoir migrer des batch existants, avec le moindre effort.
Pour intercepter et traiter les exceptions, nous avons utilisés les 2 niveaux :
1. Au niveau de chaque Step, on ajoute un StepExecutionListener, qui va convertir les exceptions en exceptions avec compteur :
Le mécanisme pour convertir les exceptions repose sur une interface ExceptionConverter :
Une implémentation par défaut DefaultExceptionConverter, servira dans la majorité des cas :
2. Au niveau du Job, on ajoute un JobExecutionListener, qui va créer et incrémenter les compteurs en utilisant un ExceptionCounter :
Pour simplifier l’utilisation de ces listeners (et les injecter par défaut) on crée :
- Une JobBuilderFactory :
Une StepbuilderFactory
Une classe d’autoconfiguration
Créer un Batch reste très simple, il suffit de remplacer les JobBuilderFatory et StepBuilderFactory standards par celles définies dans le framework :
Le mécanisme de conversion des exceptions permet de ne pas surcharger l’écriture d’un step :
La problématique des Batchs avec Prometheus
Prometheus fonctionne sur un mode Pull, c’est-à-dire, qu’il va lire les métriques sur un endpoint. Le problème avec un Batch, c’est que l’exécution est temporaire et donc que le endpoint aussi. Pour pallier ce problème, Prometheus propose d’utiliser la PushGateway. (C.f. : When to use the Pushgateway | Prometheus)
Pour installer la PushGateway, nous allons utiliser l’image docker officielle et modifier notre fichier docker-compose.yml comme suit :
Pour que notre batch puisse envoyer ses métriques vers la PushGateway, il faut ajouter la dépendance dans le pom.xml :
Puis ajouter la configuration suivante dans le fichier application.yml :
Vérifions que la PushGateway reçoit nos métriques :
Il faut maintenant configurer Prometheus pour qu’il lise les métriques des Batchs dans la PushGateway.
Dans le fichier de configuration de Prometheus (prometheus.yml) il faut indiquer sur quel endpoint lire les métriques de la PushGateway :
Puis on redémarre nos conteneurs :
Vérifions que Prometheus arrive à lire les métriques de la pushgateway :
Conclusion
Dans cet article, nous avons vu qu’il est assez simple de monitorer les exceptions qui se produisent dans une application ou un batch Spring Boot.
Il devient donc possible de suivre plus finement des problèmes techniques (Exception techniques) mais aussi des problèmes métiers (Exceptions business).
En effet, en suivant les compteurs de certaines exceptions business (comme par exemple, le nombre d’exceptions indiquant une recherche de PDL ENEDIS invalide), on peut se rendre compte d’un problème métier. Cela nous a permis par exemple de détecter un problème dans le parcours utilisateur. En analysant d’autres compteurs, l’équipe marketing a pu affiner l’offre commerciale.
Comme indiqué dans le 1er article, Prometheus dispose aussi d’un gestionnaire d’alerte (AlertManager). En ajoutant d’autres annotations aux exceptions (indiquant par exemple le niveau d’alerte souhaité), si on ajoute ce niveau d'alerte comme tag du compteur associé à l’exception, et en configurant une règle d’alerte spécifique par niveau dans Prometheus, il devient possible de lever une alerte pour certaines exceptions.
Indépendamment du monitoring, en s’appuyant sur l’AlertManager de Prometheus, il devient aussi possible de créer un système d’activateur. En utilisant des Jauges dédiées à chaque type d’activateur (avec une règle associée dans l’AlertManager de Prometheus), déclencher un activateur revient à mettre une valeur définie dans la Jauge et éteindre l’activateur revient à remettre à 0 la valeur de la Jauge.
Les possibilités sont très nombreuses, mais leur mise œuvre reste relativement simple.
N’hésitez pas lancez vous.
Références
Micrometer.io :
Spring Boot :
- Error Handling for REST with Spring | Baeldung
- Introduction to Spring Batch | Baeldung
- Spring Boot With Spring Batch | Baeldung
- Spring Boot - Production-ready Features - Spring Boot includes a number of additional features to help you monitor and man (runebook.dev)
Prometheus
- Alerting overview | Prometheus
- Alertmanager | Prometheus
- Instrumentation | Prometheus
- When to use the Pushgateway | Prometheus
- prometheus/pushgateway: Push acceptor for ephemeral and batch jobs. (github.com)
Autres: