Dans un monde où les architectures distribuées et les microservices sont devenus la norme, le besoin d’une observabilité efficace n’a jamais été aussi critique. Les systèmes modernes génèrent une quantité massive de logs, de métriques et de traces, rendant leur analyse de plus en plus complexe. C’est ici qu’OpenTelemetry intervient.
OpenTelemetry est une initiative open source soutenue par la Cloud Native Computing Foundation (CNCF). Son objectif est de fournir un standard unique pour la collecte de traces, de logs et de métriques, facilitant ainsi l’observabilité des applications cloud-native. Il est compatible avec de nombreux langages et environnements, ce qui en fait un choix privilégié pour les entreprises cherchant à unifier leur approche du monitoring.
OpenTelemetry : Un standard unifié pour les traces et métriques
Pourquoi adopter OpenTelemetry ?
- Standardisation : OpenTelemetry unifie les traces, métriques et logs sous une même spécification.
- Interopérabilité : Compatible avec Prometheus, Jaeger, Zipkin et bien d’autres outils.
- Flexibilité : OpenTelemetry permet d’utiliser divers backends sans modifier le code source. L’implémentation repose sur une API abstraite qui découple la collecte des données de leur stockage. Ainsi, une organisation peut commencer avec Jaeger pour les traces et migrer ensuite vers Zipkin ou un service managé comme AWS X-Ray sans modifier l’instrumentation de l’application. Seule la configuration de l’exportateur change.
Adoption massive : Soutenu par Microsoft, Google, et d’autres acteurs majeurs de l’industrie.
Un œil sur les composants clés
- Traces : Permet d’analyser le parcours des requêtes à travers les services.
- Métriques : Collecte et analyse les performances de l’application.
- Logs (bientôt standardisés) : Complète les informations des traces et métriques.
Comprendre les exportateurs OpenTelemetry
Un exportateur est un composant qui permet d’envoyer les données collectées par OpenTelemetry vers un backend spécifique. Grâce à l’architecture modulaire d’OpenTelemetry, vous pouvez changer d’exportateur sans modifier votre code source, en ajustant simplement la configuration.
Exemples d'exportateurs disponibles :
- Tracing :
- – Jaeger
- – Zipkin
- – AWS X-Ray
- – Grafana Tempo
- Métriques :
- – Prometheus
- – New Relic
- – Datadog
- Logs (en cours de standardisation) :
- – OpenSearch
- – Loki
L’intérêt principal de cette approche est que vous pouvez débuter avec un outil open source comme Jaeger et migrer plus tard vers une solution managée comme AWS X-Ray, sans avoir à réécrire votre instrumentation.
Exploitation des données OpenTelemetry avec Grafana
Grafana est une plateforme open source de visualisation et d’analyse des données qui peut être utilisée pour exploiter les métriques, traces et logs collectés par OpenTelemetry. Grâce à ses nombreux connecteurs, il permet d’afficher des tableaux de bord interactifs en temps réel.
Intégration d'OpenTelemetry avec Grafana
- Collecte des métriques avec Prometheus :
- OpenTelemetry peut exporter les métriques vers Prometheus.
- Grafana peut se connecter à Prometheus pour afficher ces métriques sous forme de graphiques et d’alertes.
- Visualisation des traces avec Grafana Tempo :
- OpenTelemetry peut exporter des traces vers Tempo.
- Grafana permet ensuite d’explorer les requêtes et leurs dépendances via un affichage détaillé des traces.
- Gestion des logs avec Loki :
- OpenTelemetry peut envoyer des logs vers Loki.
- Grafana permet de les visualiser et de corréler les logs avec les traces et métriques.
Grâce à cette intégration, Grafana devient un outil puissant pour centraliser l’observabilité de votre infrastructure en exploitant les données issues d’OpenTelemetry.
Tutoriel : Mise en place d'OpenTelemetry dans une API .NET
Nous allons intégrer OpenTelemetry dans une application ASP.NET Core minimaliste.
Prérequis
- .NET 6 ou supérieur installé sur votre machine
- Un outil de monitoring supporté (ici Loki)
- Un éditeur de code (Visual Studio, VS Code, JetBrains Rider)
Installation et configuration
1. Création d’un projet ASP.NET Core et ajout des dépendances nécessaires
mkdir OpenTelemetryDemo
cd OpenTelemetryDemo
dotnet new webapi
Les dépendances à installer:
dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
dotnet add package OpenTelemetry.Logs
dotnet add package OpenTelemetry.Trace
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Http
dotnet add package Microsoft.Extensions.Logging
2. Ajout d’un fichier docker-compose.yaml au projet avec le contenu suivant:
version: '3.8'
services:
dotnet-app:
build: .
ports:
- "5000:5000"
depends_on:
otel-collector:
condition: service_healthy
networks:
- observability
environment:
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
otel-collector:
image: otel/opentelemetry-collector-contrib:0.96.0
volumes:
- ./otel-collector-config.yaml:/etc/otel-config.yaml
command: ["--config=/etc/otel-config.yaml"]
ports:
- "4317:4317"
- "4318:4318"
networks:
- observability
depends_on:
loki:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:13133"]
interval: 30s
timeout: 10s
retries: 3
loki:
image: grafana/loki:2.9.4
volumes:
- loki-storage:/data/loki
ports:
- "3100:3100"
networks:
- observability
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3100/ready || exit 1"]
interval: 10s
timeout: 10s
retries: 10
grafana:
image: grafana/grafana:10.4.3
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
GF_FEATURE_TOGGLES_ENABLE: "export"
GF_LOG_LEVEL: "debug"
volumes:
- grafana-storage:/var/lib/grafana
- ./grafana-dashboards:/etc/grafana/provisioning/dashboards
networks:
- observability
depends_on:
loki:
condition: service_healthy
networks:
observability:
driver: bridge
volumes:
grafana-storage:
driver: local
loki-storage:
3. Ajout d’un fichier loki-config.yaml au projet avec le contenu suivant:
auth_enabled: false
server:
http_listen_port: 3100
ingester:
lifecycler:
address: 127.0.0.1
ring:
kvstore:
store: inmemory
replication_factor: 1
chunk_idle_period: 5m
chunk_retain_period: 30s
max_transfer_retries: 0
schema_config:
configs:
- from: 2022-01-01
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
storage_config:
boltdb_shipper:
active_index_directory: /data/loki/index
cache_location: /data/loki/cache
shared_store: filesystem
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
4. Ajout d’un fichier otel-collector-config.yaml au projet avec le contenu suivant :
receivers:
otlp:
protocols:
grpc:
http:
processors:
batch:
exporters:
loki:
endpoint: "http://loki:3100/loki/api/v1/push"
default_labels_enabled:
exporter: true
job: true
instance: true
service:
pipelines:
logs:
receivers: [otlp]
processors: [batch]
exporters: [loki]
5. Ajout du code suivant dans le Program.cs:
using OpenTelemetry.Logs;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders().AddConsole().AddOpenTelemetry(options =>
{
options.IncludeFormattedMessage = true;
options.IncludeScopes = true;
options.SetResourceBuilder(ResourceBuilder.CreateDefault()
.AddService("dotnet-console-app")
.AddAttributes(new Dictionary
{
["environment"] = "development",
["host"] = Environment.MachineName
}));
options.AddOtlpExporter(opt =>
{
opt.Endpoint = new Uri("http://otel-collector:4317"); // Envoie à OpenTelemetry Collector
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
app.UseHttpsRedirection();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
var logger = app.Services.GetRequiredService>();
logger.LogInformation("Application starting...");
app.MapGet("/weatherforecast", (ILogger logger) =>
{
logger.LogInformation("Requête reçue pour /weatherforecast");
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast");
app.Run();
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
6. Ajout d’un Dockerfile:
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
USER $APP_UID
WORKDIR /app
ENV ASPNETCORE_URLS http://+:5000
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["OpenTelemetryDemo.csproj", "."]
RUN dotnet restore "./OpenTelemetryDemo.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "./OpenTelemetryDemo.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./OpenTelemetryDemo.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "OpenTelemetryDemo.dll"]
7. Lancement des containers avec la commande :
docker-compose up --build
8. Connexion à Grafana:
On se connecte à Grafana sur l’url “http://localhost:3000”.
Il faut dans un premier temps ajouter une source de données en cliquant sur “Add your first data source”.
Choisir “Loki” et renseigner sur la page suivante l’url de connection comme ceci:

Cliquer en bas de page sur Save and Test.
Un encadré apparaît, cliquer sur “building a Dashboard”:

Cliquer sur “Add Visualization” et sélectionner la source “Loki”.
En haut à droite de la page, à la place de “Time Series”, choisir “Logs”:


En bas de page, dans les label filters, sélectionnez un label, par exemple “job” et la valeur correspondante “dotnet-console-app” qui est le nom que nous avons donné au service dans le Program.cs:


Cliquer sur “Run Query”. Nous récupérons bien nos logs:

Ouvrir une fenêtre powershell et taper la commande “curl http://localhost:5000/weatherforecast “ afin d’appeler notre api.
Comme nous avons placé un Log dans le Endpoint correspondant, un nouveau Log devrait apparaître dans Grafana.
Dans le Program.cs:

En cliquant de nouveau sur Run Query dans Grafana, mon log est bien présent 🙂
