Exempel: Använda OpenTelemetry med Prometheus, Grafana och Jaeger
I det här exemplet används Prometheus för måttsamling, Grafana för att skapa en instrumentpanel och Jaeger för att visa distribuerad spårning.
1. Skapa projektet
Skapa ett enkelt webb-API-projekt med hjälp av mallen ASP.NET Core Empty i Visual Studio eller följande .NET CLI-kommando:
dotnet new web
2. Lägg till mått och aktivitetsdefinitioner
Följande kod definierar ett nytt mått (greetings.count
) för hur många gånger API:et har anropats och en ny aktivitetskälla (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. Skapa en API-slutpunkt
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!";
}
Kommentar
API-definitionen använder inte något specifikt för OpenTelemetry. Den använder .NET-API:erna för observerbarhet.
4. Referera till OpenTelemetry-paketen
Använd NuGet Package Manager eller kommandoraden för att lägga till följande NuGet-paket:
<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>
Kommentar
Använd de senaste versionerna eftersom OTel-API:erna ständigt utvecklas.
5. Konfigurera OpenTelemetry med rätt leverantörer
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")
// Metrics provided by System.Net libraries
.AddMeter("System.Net.Http")
.AddMeter("System.Net.NameResolution")
.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();
}
});
Den här koden använder ASP.NET Core-instrumentation för att hämta mått och aktiviteter från ASP.NET Core. Den registrerar även leverantörerna Metrics
och ActivitySource
för mått respektive spårning.
Koden använder Prometheus-exportören för mått, som använder ASP.NET Core som värd för slutpunkten, så du måste också lägga till:
// Configure the Prometheus scraping endpoint
app.MapPrometheusScrapingEndpoint();
6. Kör projektet
Kör projektet och få sedan åtkomst till API:et med webbläsaren eller curl.
curl -k http://localhost:7275
Varje gång du begär sidan ökar antalet hälsningar som har gjorts. Du kan komma åt måttslutpunkten med samma bas-URL med sökvägen /metrics
.
6.1 Loggutdata
Loggningsinstruktionerna från koden matas ut med .ILogger
Som standard är konsolprovidern aktiverad så att utdata dirigeras till konsolen.
Det finns ett par alternativ för hur loggar kan tas ut från .NET:
stdout
ochstderr
utdata omdirigeras till loggfiler av containersystem som Kubernetes.- Med hjälp av loggningsbibliotek som ska integreras med ILogger inkluderar dessa Serilog eller NLog.
- Använda loggningsproviders för OTel, till exempel OTLP eller Azure Monitor-exportören som visas längre ned.
6.2 Få åtkomst till måtten
Du kan komma åt måtten med hjälp av /metrics
slutpunkten.
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
...
Måttutdata är en ögonblicksbild av måtten när slutpunkten begärs. Resultaten tillhandahålls i Prometheus expositionsformat, vilket är mänskligt läsbart men bättre förstått av Prometheus. Det ämnet beskrivs i nästa steg.
6.3 Få åtkomst till spårningen
Om du tittar på konsolen för servern ser du utdata från konsolens spårningsexportör, som matar ut informationen i ett läsbart format. Detta bör visa två aktiviteter, en från din anpassade ActivitySource
och den andra från 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
Den första är den inre anpassade aktiviteten som du skapade. Den andra skapas av ASP.NET för begäran och innehåller taggar för HTTP-begärandeegenskaperna. Du ser att båda har samma TraceId
, som identifierar en enskild transaktion och i ett distribuerat system kan användas för att korrelera spårningarna från varje tjänst som ingår i en transaktion. ID:erna överförs som HTTP-huvuden. ASP.NET Core tilldelar en TraceId
om ingen finns när den tar emot en begäran. HttpClient
innehåller rubrikerna som standard för utgående begäranden. Varje aktivitet har en SpanId
, som är kombinationen av TraceId
och SpanId
som unikt identifierar varje aktivitet. Aktiviteten Greeter
är överordnad till HTTP-aktiviteten via dess ParentSpanId
, som mappar till SpanId
HTTP-aktiviteten.
I ett senare skede matar du in dessa data i Jaeger för att visualisera de distribuerade spårningarna.
7. Samla in mått med Prometheus
Prometheus är ett databassystem för måttsamling, aggregering och tidsserier. Du konfigurerar den med måttslutpunkterna för varje tjänst och den skrapar regelbundet värdena och lagrar dem i sin tidsseriedatabas. Du kan sedan analysera och bearbeta dem efter behov.
De måttdata som exponeras i Prometheus-format är en ögonblicksbild av processens mått. Varje gång en begäran görs till måttslutpunkten rapporterar den aktuella värden. Även om aktuella värden är intressanta blir de mer värdefulla jämfört med historiska värden för att se trender och identifiera om värdena är avvikande. Vanligtvis har tjänster användningstoppar baserat på tid på dagen eller världshändelser, till exempel en semester shoppingrunda. Genom att jämföra värdena med historiska trender kan du identifiera om de är onormala eller om ett mått långsamt blir sämre med tiden.
Processen lagrar ingen historik över dessa måttögonblicksbilder. Att lägga till den funktionen i processen kan vara resurskrävande. I ett distribuerat system har du dessutom ofta flera instanser av varje nod, så du vill kunna samla in måtten från dem alla och sedan aggregera och jämföra med deras historiska värden.
7.1 Installera och konfigurera Prometheus
Ladda ned Prometheus för din plattform från https://prometheus.io/download/ och extrahera innehållet i nedladdningen.
Titta längst upp i utdata från servern som körs för att hämta portnumret för http-slutpunkten . Till exempel:
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:7275
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5212
Ändra Prometheus YAML-konfigurationsfilen för att ange porten för HTTP-skrapningsslutpunkten och ange ett lägre skrapningsintervall. Till exempel:
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"]
Starta Prometheus och titta i utdata för porten som körs på, vanligtvis 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
Öppna den här URL:en i webbläsaren. I Användargränssnittet för Prometheus bör du nu kunna fråga efter dina mått. Använd den markerade knappen i följande bild för att öppna metrics Explorer, som visar alla tillgängliga mått.
Välj måttet greetings_count
för att se ett diagram med värden.
8. Använd Grafana för att skapa en instrumentpanel för mått
Grafana är en instrumentpanelsprodukt som kan skapa instrumentpaneler och aviseringar baserat på Prometheus eller andra datakällor.
Ladda ned och installera OSS-versionen av Grafana genom https://grafana.com/oss/grafana/ att följa anvisningarna för din plattform. När grafana har installerats körs det vanligtvis på port 3000, så öppna http://localhost:3000
i webbläsaren. Du måste logga in. standardanvändarnamnet och lösenordet är båda admin
.
På hamburgermenyn väljer du anslutningar och anger sedan texten prometheus
för att välja din slutpunktstyp. Välj Skapa en Prometheus-datakälla för att lägga till en ny datakälla.
Du måste ange följande egenskaper:
- Prometheus-server-URL:
http://localhost:9090/
ändra porten efter behov
Välj Spara och testa för att verifiera konfigurationen.
När du får ett meddelande om att det har lyckats kan du konfigurera en instrumentpanel. Klicka på länken skapa en instrumentpanel som visas i popup-fönstret för meddelandet om att det lyckades.
Välj Lägg till en visualisering och välj sedan den Prometheus-datakälla som du precis lade till som datakälla.
Designern för instrumentpanelen bör visas. På den nedre halvan av skärmen kan du definiera frågan.
Välj måttet greetings_count
och välj sedan Kör frågor för att se resultatet.
Med Grafana kan du utforma avancerade instrumentpaneler som spårar valfritt antal mått.
Varje mått i .NET kan ha ytterligare dimensioner, som är nyckel/värde-par som kan användas för att partitioneras data. Alla ASP.NET mått har ett antal dimensioner som gäller för räknaren. Räknaren current-requests
från Microsoft.AspNetCore.Hosting
har till exempel följande dimensioner:
Attribut | Typ | Beskrivning | Exempel | Närvaro |
---|---|---|---|---|
method |
string |
HTTP-begärandemetod. | GET ; POST ; HEAD |
Alltid |
scheme |
string |
URI-schemat som identifierar det använda protokollet. | http ; https |
Alltid |
host |
string |
Namnet på den lokala HTTP-server som tog emot begäran. | localhost |
Alltid |
port |
int |
Port för den lokala HTTP-server som tog emot begäran. | 8080 |
Har lagts till om inte standard (80 för http eller 443 för https) |
Graferna i Grafana partitioneras vanligtvis baserat på varje unik kombination av dimensioner. Dimensionerna kan användas i Grafana-frågorna för att filtrera eller aggregera data. Om du till exempel ritar current_requests
visas värden partitionerade baserat på varje kombination av dimensioner. Om du bara vill filtrera baserat på värden lägger du till en åtgärd för Sum
och använder host
som etikettvärde.
9. Distribuerad spårning med Jaeger
I steg 6 såg du att distribuerad spårningsinformation exponerades för konsolen. Den här informationen spårar arbetsenheter med aktiviteter. Vissa aktiviteter skapas automatiskt av plattformen, till exempel den av ASP.NET för att representera hanteringen av en begäran, och bibliotek och appkod kan också skapa aktiviteter. Hälsningsexemplet har en Greeter
aktivitet. Aktiviteterna korreleras med taggarna TraceId
, SpanId
och ParentId
.
Varje process i ett distribuerat system skapar en egen ström av aktivitetsinformation, och precis som mått behöver du ett system för att samla in, lagra och korrelera aktiviteterna för att kunna visualisera det arbete som utförs för varje transaktion. Jaeger är ett projekt med öppen källkod för att aktivera den här samlingen och visualiseringen.
Ladda ned det senaste binära distributionsarkivet för Jaeger för din plattform från https://www.jaegertracing.io/download/.
Extrahera sedan nedladdningen till en lokal plats som är enkel att komma åt. Kör körbara jaeger-all-in-one(.exe) :
./jaeger-all-in-one --collector.otlp.enabled
Titta igenom konsolens utdata för att hitta porten där den lyssnar efter OTLP-trafik via gRPC. Till exempel:
{"level":"info","ts":1686963686.3854616,"caller":"otlpreceiver@v0.78.2/otlp.go:83","msg":"Starting GRPC server","endpoint":"0.0.0.0:4317"}
Det här utdata visar att den lyssnar på 0.0.0.0:4317
, så att du kan konfigurera porten som mål för OTLP-exportören.
AppSettings.json
Öppna filen för vårt projekt och lägg till följande rad och ändra porten om det är tillämpligt.
"OTLP_ENDPOINT_URL" : "http://localhost:4317/"
Starta om välkomstprocessen så att den kan hämta egenskapsändringen och börja dirigera spårningsinformation till Jaeger.
Nu bör du kunna se Jaeger-användargränssnittet från http://localhost:16686/
en webbläsare.
Om du vill se en lista över spårningar väljer du OTel-Prometheus-grafana-Jaeger
i listrutan Tjänst . Om du väljer en spårning bör ett gantt-schema över aktiviteterna visas som en del av spårningen. Om du klickar på var och en av åtgärderna visas mer information om aktiviteten.
I ett distribuerat system vill du skicka spårningar från alla processer till samma Jaeger-installation så att den kan korrelera transaktionerna i hela systemet.
Du kan göra din app lite mer intressant genom att låta den göra HTTP-anrop till sig själv.
Lägga till en
HttpClient
fabrik i programmetbuilder.Services.AddHttpClient();
Lägg till en ny slutpunkt för att göra kapslade hälsningsanrop
app.MapGet("/NestedGreeting", SendNestedGreeting);
Implementera slutpunkten så att den gör HTTP-anrop som också kan spåras. I det här fallet anropar den tillbaka till sig själv i en artificiell loop (gäller egentligen bara för demoscenarier).
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"); } }
Detta resulterar i ett mer intressant diagram med en pyramidform för begäranden, eftersom varje nivå väntar på svaret från föregående anrop.