Introduction à Prometheus en .NET
Tags: dotnet
Published: 2023-09-10 05:10:12

Dans le monde des microservices et des applications distribuées, la surveillance des performances et des erreurs est cruciale. Prometheus est un système de surveillance et d'alerte open-source, et c'est une excellente solution pour collecter des métriques depuis vos applications .NET.  
Dans cet article, je vais vous montrer comment intégrer Prometheus dans une application .NET.

## Qu'est-ce que Prometheus ?

Prometheus est un système de surveillance et d'alerte open-source. Il recueille des métriques à partir des cibles configurées à intervalles donnés, les évalue par rapport à des règles, affiche les résultats et peut déclencher des alertes si certaines conditions sont remplies. Vous trouverez toutes les ressources sur le site [prometheus.io](https://prometheus.io/).  

## Installation de Prometheus dans une application

Créons des mesures dans une application. Pour l'exemple je vais prendre un projet Blazor par défaut. Une fois le projet créé, il faut ajouter le package NuGet Prometheus. Il existe un package pour les applications Asp.NET, [Prometheus-net.AspNetCore](https://www.nuget.org/packages/prometheus-net.AspNetCore/) :  

```
dotnet add package Prometheus-net.AspNetCore
```

## Exemple d'utilisation

### Création des métriques

Prometheus propose différents types de mesures : 

- [Counters](https://github.com/prometheus-net/prometheus-net#counters) : Les compteurs augmentent seulement en valeur et sont remis à zéro lorsque le processus redémarre.
- [Gauges](https://github.com/prometheus-net/prometheus-net#gauges) : Les jauges peuvent avoir n’importe quelle valeur numérique et changer arbitrairement.
- [Histogram](https://github.com/prometheus-net/prometheus-net#histogram) : Les histogrammes suivent la taille et le nombre d’événements dans les compartiments. Cela permet un calcul agrégable des quantiles.
- [Summary](https://github.com/prometheus-net/prometheus-net#summary) : Les Summary suivent les tendances des événements au fil du temps (10 minutes par défaut).
- [durée d'une opération](https://github.com/prometheus-net/prometheus-net#measuring-operation-duration) : ils peuvent être utilisées pour signaler la durée d’une opération (en secondes) à un summary, un histogramme, une jauge ou un compteur. Encapsulez l’opération que vous souhaitez mesurer dans un bloc d’utilisation.
- [suivi des opérations en cours](https://github.com/prometheus-net/prometheus-net#tracking-in-progress-operations) : utiliser pour suivre le nombre d’opérations simultanées en cours. Encapsulez l’opération que vous souhaitez suivre dans un bloc. Par exemple : 
`Gauge.TrackInProgress()`.
- [CountExceptions](https://github.com/prometheus-net/prometheus-net#counting-exceptions) : Je pense que vous voyez à quoi ça peut servir ;)  
Il faut aller voir la doc pour avoir chaque particularités.  

Je ne vais pas faire un exemple par chaque mesure, je vais faire un exemple avec un **Counter**. 

#### Le Compteur

Dans cet exemple je vais créer ajouter un compteur et il comptabilisera chaque click du compteur du projet Blazor.  
Le code de la vue avec notre compteur.

```html
@page "/counter"
@using Prometheus  @* Ajout de l'importation ici *@

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
        IncrementCounter.Inc();
    }
    
    private static readonly Counter IncrementCounter = Metrics.CreateCounter("counter_click_increments_total", "Nombre total de click.");
}
```

Examinons ça d'un peu plus près.  
![](https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExbXN4dnU2N2JtN3piNTlpenlyMGlnc2p0N3dpc3VhNmVhYTE4dmt6MyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/Gpf8A8aX2uWAg/giphy-downsized.gif)  

Je crée un nouveau compteur `Counter IncrementCounter`.
Je lui donne comme nom _(ou comme "label")_ : **counter_click_increments_total**.
J'indique une description : "Nombre total de click.".
Ces informations se retrouveront dans l'exposition, voici l'exemple :  

```
# HELP counter_click_increments_total Nombre total de click.
# TYPE counter_click_increments_total counter
counter_click_increments_total 5
```

C'est cette ligne : `IncrementCounter.Inc();` qui fait l'incrément du compteur.  
Bon, maintenant que nous avons une mesure, comment l'exposer au monde, et surtout que Prometheus puisse venir faire sa récolte.

### Exposition des métriques

Pour exposer les métriques pour que Prometheus puisse les récupérer, il faut ajoutez dans `Program.cs`, ou dans la méthode `Configure` de la classe `Startup` _(dépend de votre projet)_ :

```csharp
app.UseRouting();

// Active la collecte des métriques HTTP
app.UseHttpMetrics(); 

// ... (du code, du code,...)

app.UseEndpoints(endpoints =>
{
	endpoints.MapMetrics(); // Expose les métriques à l'endpoint /metrics
	endpoints.MapControllers();
});
```

Petite explication.  
- `app.UseHttpMetrics()` : ce middleware ajoute plusieurs métriques par défaut. Pas obligatoire d'avoir ce middleware.  
Par exemple :  

```
# HELP http_requests_received_total Provides the count of HTTP requests that have been processed by the ASP.NET Core pipeline.
# TYPE http_requests_received_total counter
http_requests_received_total{code="200",method="POST",controller="",action="",page="",endpoint="/_blazor/negotiate"} 1
http_requests_received_total{code="200",method="GET",controller="",action="",page="",endpoint="/_blazor/initializers/"} 1
http_requests_received_total{code="200",method="HEAD",controller="",action="",page="/_Host",endpoint=""} 1
http_requests_received_total{code="200",method="GET",controller="",action="",page="/_Host",endpoint=""} 1
```
**Note** : Il doit être placé **après** `app.UseRouting()`.  

- `endpoints.MapMetrics()` : indique au serveur qu'il doit exposer un endpoint pour les mesures. Par défaut c'est `/metrics`.  
Maintenant que nous avons nos mesures, que nous les exposons, il ne reste plus qu'à installer et configurer notre serveur Prometheus.

## Prometheus

Pour l'article, je vais utiliser l'installation de Prometheus en local pour Windows. Toutes les releases sont accessible sur [prometheus.io/download](https://prometheus.io/download/).  

## Configuration de Prometheus

Il faut indiquer à Prometheus où trouver les métriques. Il faut éditer le fichier `prometheus.yml` et ajouter notre application à la liste des `targets`.  
Voici le fichier de config complet :

```yaml
# my global config
global:
  scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
    - static_configs:
        - targets:
          # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: "Prometheus"

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
      - targets: ["localhost:9090"]

  - job_name: 'blazorapp'
    scrape_interval: 30s
    scheme: https # Utilise le schéma HTTPS au lieu de HTTP par défaut
    static_configs:
      - targets: ['localhost:7116']
```

Maintenant, intéressons nous à notre application.

```yaml
scrape_configs:
# ...autres jobs...

  - job_name: 'blazorapp'
    scrape_interval: 30s
    scheme: https # Utilise le schéma HTTPS au lieu de HTTP par défaut
    static_configs:
      - targets: ['localhost:7116']
```

##### Configuration de la tâche de scraping (job)  
- **job_name** : c'est le nom qui sert à regrouper un ensemble de cibles sous un même nom. Possible de scrap plusieurs cibles.
- **scheme** : ce champ spécifie que le protocole HTTPS doit être utilisé pour le scraping. Par défaut, Prometheus utilise HTTP.
- **scrape_interval** : L'intervalle à lequel Prometheus va "scraper" (récupérer) les métriques depuis les cibles définies. Dans ce cas, l'intervalle est de 30 secondes. Si cette option n'est pas spécifiée, l'intervalle par défaut global sera utilisé.  

En complément, il est possible d'utiliser plusieurs formats pour indiquer cette durée. Voici quelques exemples :
- **Secondes** : Utilisez `s` pour indiquer des secondes. Par exemple, `scrape_interval: 15s` définit un intervalle de scraping de 15 secondes.
- **Minutes** : Utilisez `m` pour indiquer des minutes. Par exemple, `scrape_interval: 5m` définit un intervalle de scraping de 5 minutes.
- **Heures** : Utilisez `h` pour indiquer des heures. Par exemple, `scrape_interval: 2h` définit un intervalle de scraping de 2 heures.
- **Jours** : Utilisez `d` pour indiquer des jours, bien que ce ne soit généralement pas recommandé pour des opérations de scraping. Par exemple, `scrape_interval: 1d` définit un intervalle de scraping d'un jour.
- **Semaines** : Utilisez `w` pour indiquer des semaines, bien que ce ne soit généralement pas recommandé pour des opérations de scraping. Par exemple, `scrape_interval: 1w` définit un intervalle de scraping d'une semaine.
- **Unité multiple** : Vous pouvez également combiner plusieurs unités pour indiquer une durée précise. Par exemple, `scrape_interval: 1h30m` définit un intervalle de scraping d'une heure et trente minutes.
Notez que si vous omettez l'unité, Prometheus supposera que la valeur est en millisecondes, ce qui n'est généralement pas ce que vous voulez pour un intervalle de scraping.
Il est important de choisir un `scrape_interval` approprié en fonction des besoins de votre système et des métriques que vous collectez. Un intervalle trop court pourrait surcharger le système, tandis qu'un intervalle trop long pourrait manquer des informations importantes.

##### Configuration des cibles de scraping

- **static_configs** : Cette section configure une liste statique de cibles pour le scraping. D'autres types de découverte de cibles (comme la découverte de services via Kubernetes, par exemple) peuvent également être configurés, mais ici une configuration statique est utilisée.

- **targets** : ['localhost:7116']: Liste des cibles à scraper. Chaque cible est représentée par une chaîne de caractères sous la forme <adresse_ip>:<port>. Dans l'exemple, je n'ai qu'une seule cible.

![](https://raw.githubusercontent.com/AnthonyRyck/ctrl-alt-suppr/main/ImgBlog/Prometheus/01_Prometheus_Targets.jpg)

### Un Graph ?

Le serveur Prometheus permet de créer des graphs sur les données récoltées. Il faut juste mettre le nom de notre label et Execute !

![](https://raw.githubusercontent.com/AnthonyRyck/ctrl-alt-suppr/main/ImgBlog/Prometheus/02_Prometheus_GraphCounter.jpg)

## Conclusion

Vous avez maintenant une application .NET qui expose des métriques que Prometheus peut collecter. Bien sûr, cet exemple est très basique. Vous pouvez créer des compteurs, des jauges, des histogrammes et d'autres types de métriques pour suivre presque tout ce que vous pouvez imaginer dans votre application.

La surveillance est un aspect essentiel de tout système de production. Avec Prometheus et C#, vous avez les outils nécessaires pour construire un système de surveillance robuste qui peut évoluer avec vos besoins.