Exemple : Utiliser OpenTelemetry avec Prometheus, Grafana et Jaeger
Cet exemple utilise Prometheus pour effectuer des collections de métriques, Grafana pour créer des tableaux de bord et Jaeger pour afficher des suivis distribués.
1. Créer le projet
Créez un projet d’API web simple en utilisant le modèle ASP.NET Core vide dans Visual Studio ou la commande CLI .NET suivante :
dotnet new web
2. Ajouter des métriques et des définitions d’activité
Le code suivant définit une nouvelle métrique (greetings.count
) correspondant au nombre de fois où l’API a été appelée et une nouvelle source d’activité (OtPrGrYa.Example
).
// Custom metrics for the application
var greeterMeter = new Meter("OtPrGrYa.Example", "1.0.0");
var countGreetings = greeterMeter.CreateCounter<int>("greetings.count", description: "Counts the number of greetings");
// Custom ActivitySource for the application
var greeterActivitySource = new ActivitySource("OtPrGrJa.Example");
3. Créer un point de terminaison d’API
app.MapGet("/", SendGreeting);
async Task<String> SendGreeting(ILogger<Program> logger)
{
// Create a new Activity scoped to the method
using var activity = greeterActivitySource.StartActivity("GreeterActivity");
// Log a message
logger.LogInformation("Sending greeting");
// Increment the custom counter
countGreetings.Add(1);
// Add a tag to the Activity
activity?.SetTag("greeting", "Hello World!");
return "Hello World!";
}
Notes
La définition d’API n’utilise aucun élément spécifique à OpenTelemetry. Elle utilise les API .NET pour l’observabilité.
4. Référencer les packages OpenTelemetry
Utilisez le Gestionnaire de package NuGet ou la ligne de commande pour ajouter les packages NuGet suivants :
<ItemGroup>
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
<PackageReference Include="OpenTelemetry.Exporter.Zipkin" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
</ItemGroup>
Notes
Utilisez les versions les plus récentes, car les API d’OTel sont en constante évolution.
5. Configurer OpenTelemetry avec les fournisseurs appropriés
var tracingOtlpEndpoint = builder.Configuration["OTLP_ENDPOINT_URL"];
var otel = builder.Services.AddOpenTelemetry();
// Configure OpenTelemetry Resources with the application name
otel.ConfigureResource(resource => resource
.AddService(serviceName: builder.Environment.ApplicationName));
// Add Metrics for ASP.NET Core and our custom metrics and export to Prometheus
otel.WithMetrics(metrics => metrics
// Metrics provider from OpenTelemetry
.AddAspNetCoreInstrumentation()
.AddMeter(greeterMeter.Name)
// Metrics provides by ASP.NET Core in .NET 8
.AddMeter("Microsoft.AspNetCore.Hosting")
.AddMeter("Microsoft.AspNetCore.Server.Kestrel")
.AddPrometheusExporter());
// Add Tracing for ASP.NET Core and our custom ActivitySource and export to Jaeger
otel.WithTracing(tracing =>
{
tracing.AddAspNetCoreInstrumentation();
tracing.AddHttpClientInstrumentation();
tracing.AddSource(greeterActivitySource.Name);
if (tracingOtlpEndpoint != null)
{
tracing.AddOtlpExporter(otlpOptions =>
{
otlpOptions.Endpoint = new Uri(tracingOtlpEndpoint);
});
}
else
{
tracing.AddConsoleExporter();
}
});
Ce code utilise l’instrumentation ASP.NET Core pour obtenir des métriques et des activités à partir d’ASP.NET Core. Il enregistre également les fournisseurs Metrics
et ActivitySource
pour les métriques et le suivi respectivement.
Le code utilise l’exporter Prometheus pour les métriques, qui utilise ASP.NET Core afin d’héberger le point de terminaison. Vous devez donc également ajouter :
// Configure the Prometheus scraping endpoint
app.MapPrometheusScrapingEndpoint();
6. Exécuter le projet
Exécutez le projet, puis accédez à l’API avec le navigateur ou curl.
curl -k http://localhost:7275
Chaque fois que vous demandez la page, elle incrémente le nombre de messages d’accueil effectués. Vous pouvez accéder au point de terminaison des métriques en utilisant la même URL de base, avec le chemin d’accès /metrics
.
6.1 Sortie du journal
Les instructions de journalisation du code sont générées à l’aide de ILogger
. Par défaut, le Fournisseur Console est activé afin que la sortie soit dirigée vers la console.
Il existe deux possibilités pour effectuer une sortie de journaux à partir de .NET :
- Les sorties
stdout
etstderr
sont redirigées vers des fichiers journaux par des systèmes de conteneur tels que Kubernetes. - À l’aide de bibliothèques de journalisation, incluant Serilog ou NLog, qui s’intègrent à ILogger.
- À l’aide de fournisseurs de journalisation pour OTel, comme OTLP ou l’exporter Azure Monitor, comme illustré ci-dessous.
6.2 Accéder aux métriques
Vous pouvez accéder aux métriques en tirant parti du point de terminaison /metrics
.
curl -k https://localhost:7275/
Hello World!
curl -k https://localhost:7275/metrics
# TYPE greetings_count counter
# HELP greetings_count Counts the number of greetings
greetings_count 1 1686894204856
# TYPE current_connections gauge
# HELP current_connections Number of connections that are currently active on the server.
current_connections{endpoint="127.0.0.1:7275"} 1 1686894204856
current_connections{endpoint="[::1]:7275"} 0 1686894204856
current_connections{endpoint="[::1]:5212"} 1 1686894204856
...
La sortie des métriques est une capture instantanée des métriques au moment où le point de terminaison est demandé. Les résultats sont fournis au format d’exposition Prometheus, lisible par l’utilisateur et mieux compris par Prometheus. Ce sujet est abordé dans la phase suivante.
6.3 Accéder au suivi
Si vous examinez la console du serveur, vous verrez la sortie de l’exporter de trace de console, qui génère les informations dans un format lisible par l’utilisateur. Elle doit afficher deux activités, l’une à partir de votre ActivitySource
personnalisé et l’autre d’ASP.NET Core :
Activity.TraceId: 2e00dd5e258d33fe691b965607b91d18
Activity.SpanId: 3b7a891f55b97f1a
Activity.TraceFlags: Recorded
Activity.ParentSpanId: 645071fd0011faac
Activity.ActivitySourceName: OtPrGrYa.Example
Activity.DisplayName: GreeterActivity
Activity.Kind: Internal
Activity.StartTime: 2023-06-16T04:50:26.7675469Z
Activity.Duration: 00:00:00.0023974
Activity.Tags:
greeting: Hello World!
Resource associated with Activity:
service.name: OTel-Prometheus-Grafana-Jaeger
service.instance.id: e1afb619-bc32-48d8-b71f-ee196dc2a76a
telemetry.sdk.name: opentelemetry
telemetry.sdk.language: dotnet
telemetry.sdk.version: 1.5.0
Activity.TraceId: 2e00dd5e258d33fe691b965607b91d18
Activity.SpanId: 645071fd0011faac
Activity.TraceFlags: Recorded
Activity.ActivitySourceName: Microsoft.AspNetCore
Activity.DisplayName: /
Activity.Kind: Server
Activity.StartTime: 2023-06-16T04:50:26.7672615Z
Activity.Duration: 00:00:00.0121259
Activity.Tags:
net.host.name: localhost
net.host.port: 7275
http.method: GET
http.scheme: https
http.target: /
http.url: https://localhost:7275/
http.flavor: 1.1
http.user_agent: curl/8.0.1
http.status_code: 200
Resource associated with Activity:
service.name: OTel-Prometheus-Grafana-Jaeger
service.instance.id: e1afb619-bc32-48d8-b71f-ee196dc2a76a
telemetry.sdk.name: opentelemetry
telemetry.sdk.language: dotnet
telemetry.sdk.version: 1.5.0
La première est l’activité personnalisée interne que vous avez créée. La seconde est créée par ASP.NET pour la requête et inclut des balises pour les propriétés de requête HTTP. Vous verrez que les deux ont le même TraceId
, qui identifie une seule transaction et dans un système distribué qui peut être utilisé pour mettre en corrélation les traces de chaque service impliqué dans une transaction. Les ID sont transmis sous forme d’en-têtes HTTP. ASP.NET Core attribue un TraceId
si aucun n’est présent lorsqu’il reçoit une requête. HttpClient
inclut les en-têtes par défaut sur des requêtes sortantes. Chaque activité a un SpanId
, qui est la combinaison de TraceId
et SpanId
permettant d’identifier chaque activité de manière unique. L’activité Greeter
est apparentée à l’activité HTTP via son ParentSpanId
, qui mappe vers SpanId
de l’activité HTTP.
Dans une phase ultérieure, vous allez alimenter ces données dans Jaeger pour visualiser les traces distribuées.
7. Collection de métriques avec Prometheus
Prometheus est un système de collection de métriques, d’agrégation et de base de données de série chronologique. Vous le configurez avec les points de terminaison de métrique pour chaque service pour qu’il récupère régulièrement les valeurs et les stocke dans sa base de données de série chronologique. Vous pouvez ensuite les analyser et les traiter en fonction des besoins.
Les données de métriques exposées au format Prometheus sont une capture instantanée dans le temps des métriques du processus. Chaque fois qu’une requête est envoyée au point de terminaison de métriques, elle signale les valeurs actuelles. Bien que les valeurs actuelles soient intéressantes, elles deviennent plus utiles que les valeurs historiques pour voir des tendances et détecter des valeurs inhabituelles. Souvent, les services connaissent des pics d’utilisation en fonction de l’heure de la journée ou des événements mondiaux, comme une frénésie d’achats lors des fêtes. En comparant les valeurs aux tendances historiques, vous pouvez détecter si elles sont inhabituelles ou si une métrique se détériore lentement au fil du temps.
Le processus ne stocke aucun historique de ces captures instantanées de métriques. L’ajout de cette fonctionnalité au processus peut consommer beaucoup de ressources. En outre, vous disposez généralement de plusieurs instances de chaque nœud dans un système distribué. Vous souhaitez donc pouvoir y collecter toutes les métriques, puis les agréger et les comparer avec leurs valeurs historiques.
7.1 Installation et configuration de Prometheus
Téléchargez Prometheus pour votre plateforme à partir de https://prometheus.io/download/ et extrayez le contenu du téléchargement.
Examinez la partie supérieure de la sortie de votre serveur en cours d’exécution afin d’obtenir le numéro de port du point de terminaison http. Par exemple :
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:7275
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5212
Modifiez le fichier de configuration YAML Prometheus pour spécifier le port de votre point de terminaison de scraping HTTP et définissez un intervalle de scraping inférieur. Par exemple :
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'.
scrape_interval: 1s # poll very quickly for a more responsive demo
static_configs:
- targets: ["localhost:5212"]
Démarrez Prometheus et recherchez dans la sortie le port sur lequel il s’exécute, en général 9090 :
>prometheus.exe
...
ts=2023-06-16T05:29:02.789Z caller=web.go:562 level=info component=web msg="Start listening for connections" address=0.0.0.0:9090
Ouvrez cette URL dans votre navigateur. Dans l’interface utilisateur Prometheus, vous devriez maintenant pouvoir interroger vos métriques. Utilisez le bouton mis en surbrillance dans l’image suivante pour ouvrir Metrics Explorer qui affiche toutes les métriques disponibles.
Sélectionnez la métrique greetings_count
pour afficher un graphique des valeurs.
8. Utiliser Grafana pour la création d’un tableau de bord de métriques
Grafana est un produit de création de tableaux de bord qui peut créer des tableaux de bord et des alertes basés sur Prometheus ou d’autres sources de données.
Téléchargez et installez la version OSS de Grafana https://grafana.com/oss/grafana/ en suivant les instructions de votre plateforme. Une fois installée, la plateforme Grafana est généralement exécutée sur le port 3000. Ouvrez donc http://localhost:3000
dans votre navigateur. Vous devez vous connecter. Le nom d’utilisateur et le mot de passe par défaut sont tous deux admin
.
Dans le menu « hamburger », choisissez Connexions, puis entrez le texte prometheus
pour sélectionner votre type de point de terminaison. Sélectionnez Créer une source de données Prometheus pour ajouter une nouvelle source de données.
Vous devez définir les propriétés suivantes :
- URL du serveur Prometheus :
http://localhost:9090/
modifiant le port, le cas échéant
Sélectionnez Enregistrer et Tester pour vérifier la configuration.
Une fois que vous obtenez un message de réussite, vous pouvez configurer un tableau de bord. Cliquez sur le lien générer un tableau de bord affiché dans la fenêtre contextuelle du message de réussite.
Sélectionnez Ajouter une visualisation, puis choisissez la source de données Prometheus que vous venez d’ajouter comme source de données.
Le concepteur de panneau de tableau de bord doit s’afficher. Dans la moitié inférieure de l’écran, vous pouvez définir la requête.
Sélectionnez la métrique greetings_count
, puis sélectionnez Exécuter des requêtes pour afficher les résultats.
Vous pouvez, avec Grafana, concevoir des tableaux de bord sophistiqués qui effectuent le suivi d’un nombre quelconque de métriques.
Chaque métrique dans .NET peut avoir des dimensions supplémentaires, qui sont des paires clé-valeur pouvant être utilisées pour partitionner les données. Les métriques ASP.NET présentent toutes un certain nombre de dimensions applicables au compteur. Par exemple, le compteur current-requests
de Microsoft.AspNetCore.Hosting
a les dimensions suivantes :
Attribut | Type | Description | Exemples | Présence |
---|---|---|---|---|
method |
string |
Méthode de requête HTTP. | GET ; POST ; HEAD |
Toujours |
scheme |
string |
Schéma d’URI identifiant le protocole utilisé. | http ; https |
Toujours |
host |
string |
Nom du serveur HTTP local qui a reçu la requête. | localhost |
Toujours |
port |
int |
Port du serveur HTTP local qui a reçu la requête. | 8080 |
Ajouté s’il ne s’agit pas de la valeur par défaut (80 pour http ou 443 pour https) |
Les graphiques dans Grafana sont généralement partitionnés en fonction de chaque combinaison unique de dimensions. Les dimensions peuvent être utilisées dans les requêtes Grafana pour filtrer ou agréger les données. Par exemple, si vous représentez un graphique current_requests
, vous verrez des valeurs partitionnée basées sur chaque combinaison de dimensions. Pour filtrer uniquement en fonction de l’hôte, ajoutez une opération de Sum
et utilisez host
en tant que valeur d’étiquette.
9. Suivi distribué avec Jaeger
À l’Étape 6, vous avez vu que les informations de suivi distribué étaient exposées dans la console. Ces informations effectuent un suivi des unités de travail avec des activités. Certaines activités sont créées automatiquement par la plateforme, comme celle de ASP.NET pour représenter la gestion d’une requête. Les bibliothèques et le code d’application peuvent également créer des activités. L’exemple « greetings » a une activité Greeter
. Les activités sont corrélées à l’aide des balises TraceId
, SpanId
et ParentId
.
Chaque processus dans un système distribué génère son propre flux d’informations d’activité et, comme pour les métriques, vous avez besoin d’un système pour collecter, stocker et mettre en corrélation les activités afin de pouvoir visualiser le travail effectué pour chaque transaction. Jaeger est un projet open source permettant d’activer cette collection et cette visualisation.
Téléchargez la dernière archive de distribution binaire de Jaeger pour votre plateforme depuis https://www.jaegertracing.io/download/.
Extrayez ensuite le téléchargement vers un emplacement local facile d’accès. Exécutez le fichier exécutable jaeger-all-in-one(.exe) :
./jaeger-all-in-one --collector.otlp.enabled
Parcourez la sortie de la console pour rechercher le port où elle écoute le trafic OTLP via gRPC. Par exemple :
{"level":"info","ts":1686963686.3854616,"caller":"otlpreceiver@v0.78.2/otlp.go:83","msg":"Starting GRPC server","endpoint":"0.0.0.0:4317"}
Cette sortie vous indique qu’elle écoute sur 0.0.0.0:4317
. Vous pouvez donc configurer ce port comme destination pour votre exporter OTLP.
Ouvrez le fichier AppSettings.json
pour notre projet, puis ajoutez la ligne suivante, en modifiant le port le cas échéant.
"OTLP_ENDPOINT_URL" : "http://localhost:4317/"
Redémarrez le processus d’accueil afin qu’il puisse récupérer la modification de propriété et commencer à diriger les informations de suivi vers Jaeger.
Vous devez maintenant être en mesure de voir l’interface utilisateur Jaeger à l’adresse http://localhost:16686/
à partir d’un navigateur web.
Pour afficher la liste de traces, sélectionnez OTel-Prometheus-grafana-Jaeger
dans la liste déroulante Service. La sélection d’une trace devrait afficher un diagramme de Gantt des activités faisant partie de cette trace. Cliquez sur chacune des opérations pour afficher d’autres informations sur l’activité.
Dans un système distribué, vous souhaitez envoyer des traces de tous les processus vers la même installation Jaeger afin qu’elle puisse mettre en corrélation les transactions dans le système.
Vous pouvez rendre votre application un peu plus intéressante en lui faisant passer des appels HTTP à elle-même.
Ajouter une fabrique
HttpClient
à l’applicationbuilder.Services.AddHttpClient();
Ajouter un nouveau point de terminaison pour effectuer des appels d’accueil imbriqués
app.MapGet("/NestedGreeting", SendNestedGreeting);
Implémentez le point de terminaison afin qu’il effectue des appels HTTP qui peuvent également faire l’objet d’une trace. Dans ce cas, il se rappelle dans une boucle artificielle (applicable uniquement aux scénarios de démonstration).
async Task SendNestedGreeting(int nestlevel, ILogger<Program> logger, HttpContext context, IHttpClientFactory clientFactory) { // Create a new Activity scoped to the method using var activity = greeterActivitySource.StartActivity("GreeterActivity"); if (nestlevel <= 5) { // Log a message logger.LogInformation("Sending greeting, level {nestlevel}", nestlevel); // Increment the custom counter countGreetings.Add(1); // Add a tag to the Activity activity?.SetTag("nest-level", nestlevel); await context.Response.WriteAsync($"Nested Greeting, level: {nestlevel}\r\n"); if (nestlevel > 0) { var request = context.Request; var url = new Uri($"{request.Scheme}://{request.Host}{request.Path}?nestlevel={nestlevel - 1}"); // Makes an http call passing the activity information as http headers var nestedResult = await clientFactory.CreateClient().GetStringAsync(url); await context.Response.WriteAsync(nestedResult); } } else { // Log a message logger.LogError("Greeting nest level {nestlevel} too high", nestlevel); await context.Response.WriteAsync("Nest level too high, max is 5"); } }
Il en résulte un graphique plus intéressant avec une forme pyramidale pour les requêtes, car chaque niveau attend la réponse de l’appel précédent.