Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Dieser Artikel gilt für: ✔️ .NET 6.0 und höher ✔️ .NET Framework 4.6.1 und höher
Instrumentierter Code kann numerische Messungen aufzeichnen, aber die Messungen müssen in der Regel aggregiert, übertragen und gespeichert werden, um nützliche Metriken für die Überwachung zu erstellen. Das Verfahren zum Aggregieren, Übertragen und Speichern von Daten wird als Sammlung bezeichnet. Dieses Lernprogramm zeigt mehrere Beispiele für das Sammeln von Metriken:
- Auffüllen von Metriken in Grafana mit OpenTelemetry und Prometheus.
- Anzeigen von Metriken in Echtzeit mit
dotnet-counters - Erstellen eines benutzerdefinierten Sammlungstools mithilfe der zugrunde liegenden .NET-API MeterListener .
Weitere Informationen zu benutzerdefinierten Metrikinstrumentation und -optionen finden Sie unter Vergleichen von Metrik-APIs.
Voraussetzungen
- .NET 6.0 SDK oder höher
Erstellen einer Beispiel-App
Bevor Metriken erfasst werden können, müssen Messungen erstellt werden. In diesem Lernprogramm wird eine App erstellt, die über grundlegende Metrikinstrumentation verfügt. Die . NET-Laufzeit verfügt auch über verschiedene integrierte Metriken. Weitere Informationen zum Erstellen neuer Metriken mithilfe der System.Diagnostics.Metrics.Meter API finden Sie im Lernprogramm zur Instrumentierung.
dotnet new console -o metric-instr
cd metric-instr
dotnet add package System.Diagnostics.DiagnosticSource
Ersetzen Sie den Inhalt von Program.cs durch den folgenden Code.
using System.Diagnostics.Metrics;
class Program
{
static Meter s_meter = new("HatCo.HatStore", "1.0.0");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>("hats-sold");
static void Main(string[] args)
{
var rand = Random.Shared;
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable)
{
//// Simulate hat selling transactions.
Thread.Sleep(rand.Next(100, 2500));
s_hatsSold.Add(rand.Next(0, 1000));
}
}
}
Im vorherigen Code wird der Verkauf von Hüten in zufälligen Intervallen und zufälligen Zeiten simuliert.
Anzeigen von Metriken mit Dotnet-Counters
dotnet-counters ist ein Befehlszeilentool , das Livemetriken für .NET Core-Apps bei Bedarf anzeigen kann. Es ist keine Einrichtung erforderlich, sodass es nützlich ist für Ad-hoc-Untersuchungen oder um zu überprüfen, ob die Metrikinstrumentation funktioniert. Es funktioniert sowohl mit System.Diagnostics.Metrics-basierten APIs als auch mit EventCounters.
Wenn das Tool dotnet-counters nicht installiert ist, führen Sie den folgenden Befehl aus:
dotnet tool update -g dotnet-counters
Wenn Ihre App eine version von .NET ausführt, die älter als .NET 9 ist, sieht die Ausgabebenutzeroberfläche von dotnet-counters etwas anders aus als unten. Weitere Informationen finden Sie unter dotnet-counters .
Während die Beispiel-App ausgeführt wird, starten Sie dotnet-counters. Der folgende Befehl zeigt ein Beispiel für die dotnet-counters Überwachung aller Metriken des HatCo.HatStore Zählers. Bei dem Typnamen wird die Groß- und Kleinschreibung berücksichtigt. Unsere Beispiel-App wurde metric-instr.exe, ersetzen Sie dies durch den Namen Ihrer Beispiel-App.
dotnet-counters monitor -n metric-instr HatCo.HatStore
Dadurch werden Informationen angezeigt, die mit denen der folgenden Ausgabe vergleichbar sind:
Press p to pause, r to resume, q to quit.
Status: Running
[HatCo.HatStore]
hats-sold (Count / 1 sec) 4
dotnet-counters kann mit einem anderen Satz von Metriken ausgeführt werden, um einige der integrierten Instrumentierung aus der .NET-Laufzeit anzuzeigen:
dotnet-counters monitor -n metric-instr
Dadurch werden Informationen angezeigt, die mit denen der folgenden Ausgabe vergleichbar sind:
System.Runtime
Press p to pause, r to resume, q to quit.
Status: Running
Name Current Value
[System.Runtime]
dotnet.assembly.count ({assembly}) 11
dotnet.gc.collections ({collection})
gc.heap.generation
------------------
gen0 0
gen1 0
gen2 0
dotnet.gc.heap.total_allocated (By) 1,376,024
dotnet.gc.last_collection.heap.fragmentation.size (By)
gc.heap.generation
------------------
gen0 0
gen1 0
gen2 0
loh 0
poh 0
dotnet.gc.last_collection.heap.size (By)
gc.heap.generation
------------------
gen0 0
gen1 0
gen2 0
loh 0
poh 0
dotnet.gc.last_collection.memory.committed_size (By) 0
dotnet.gc.pause.time (s) 0
dotnet.jit.compilation.time (s) 0.253
dotnet.jit.compiled_il.size (By) 79,536
dotnet.jit.compiled_methods ({method}) 743
dotnet.monitor.lock_contentions ({contention}) 0
dotnet.process.cpu.count ({cpu}) 22
dotnet.process.cpu.time (s)
cpu.mode
--------
system 0.125
user 46.453
dotnet.process.memory.working_set (By) 34,447,360
dotnet.thread_pool.queue.length ({work_item}) 0
dotnet.thread_pool.thread.count ({thread}) 0
dotnet.thread_pool.work_item.count ({work_item}) 0
dotnet.timer.count ({timer}) 0
Weitere Informationen finden Sie unter dotnet-counters. Weitere Informationen zu Metriken in .NET finden Sie unter integrierten Metriken.
Anzeigen von Metriken in Grafana mit OpenTelemetry und Prometheus
Überblick
- Ist ein anbieterneutrales Open-Source-Projekt, das von der Cloud Native Computing Foundation unterstützt wird.
- Standardisiert das Generieren und Sammeln von Telemetrie für cloudeigene Software.
- Funktioniert mit .NET mithilfe der .NET-Metrik-APIs.
- Wird von Azure Monitor und vielen APM-Anbietern unterstützt.
In diesem Lernprogramm wird eine der Integrationen gezeigt, die für OpenTelemetry-Metriken mit den PROJEKTEN OSS Prometheus und Grafana zur Verfügung stehen. Der Metrikdatenfluss:
Die .NET-Metrik-APIs zeichnen Maßangaben aus der Beispiel-App auf.
Die in der App ausgeführte OpenTelemetry-Bibliothek aggregiert die Messungen.
Die Prometheus-Exporterbibliothek stellt die aggregierten Daten über einen HTTP-Metrikenendpunkt zur Verfügung. "Exporter" ist der Begriff, den OpenTelemetry für die Bibliotheken verwendet, die Telemetriedaten an herstellerspezifische Back-Ends übertragen.
Ein Prometheus-Server:
- Fragt den Metrikendpunkt ab.
- Liest die Daten
- Speichert die Daten in einer Datenbank für langfristige Persistenz. Prometheus bezeichnet in diesem Zusammenhang das Lesen und Speichern von Daten als Scraping eines Endpunkts.
- Kann auf einem anderen Computer ausgeführt werden
Der Grafana-Server:
- Fragt die in Prometheus gespeicherten Daten ab und zeigt sie auf einem webbasierten Überwachungsdashboard an.
- Kann auf einem anderen Computer ausgeführt werden.
Konfigurieren der Beispiel-App für die Verwendung des Prometheus-Exporters von OpenTelemetry
Fügen Sie der Beispiel-App einen Verweis auf den OpenTelemetry Prometheus-Exporter hinzu:
dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener --prerelease
Hinweis
In diesem Tutorial wird ein Vorversionsbuild der Prometheus-Unterstützung von OpenTelemetry verwendet, der zum Zeitpunkt der Niederschrift verfügbar war.
Aktualisieren sie Program.cs mit der OpenTelemetry-Konfiguration:
using OpenTelemetry;
using OpenTelemetry.Metrics;
using System.Diagnostics.Metrics;
class Program
{
static Meter s_meter = new("HatCo.HatStore", "1.0.0");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>(
name: "hats-sold",
unit: "Hats",
description: "The number of hats sold in our store");
static void Main(string[] args)
{
using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder()
.AddMeter("HatCo.HatStore")
.AddPrometheusHttpListener(options => options.UriPrefixes = new string[] { "http://localhost:9184/" })
.Build();
var rand = Random.Shared;
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable)
{
//// Simulate hat selling transactions.
Thread.Sleep(rand.Next(100, 2500));
s_hatsSold.Add(rand.Next(0,1000));
}
}
}
Im vorhergehenden Code:
-
AddMeter("HatCo.HatStore")konfiguriert OpenTelemetry so, dass alle Metriken übertragen werden, die vom in der App definierten Meter gesammelt wurden. -
AddPrometheusHttpListenerkonfiguriert OpenTelemetry für:- Verfügbarmachen des Prometheus-Metrikendpunkts am Port
9184 - Verwenden Sie den HttpListener.
- Verfügbarmachen des Prometheus-Metrikendpunkts am Port
Weitere Informationen zu OpenTelemetry-Konfigurationsoptionen finden Sie in der OpenTelemetry-Dokumentation . In der OpenTelemetry-Dokumentation werden Hostingoptionen für ASP.NET Apps angezeigt.
Führen Sie die App aus, und lassen Sie sie laufen, damit Messungen gesammelt werden können:
dotnet run
Einrichten und Konfigurieren von Prometheus
Führen Sie die ersten Schritte aus, um einen Prometheus-Server einzurichten und zu bestätigen, dass er funktioniert.
Ändern Sie die prometheus.yml Konfigurationsdatei so, dass Prometheus den Metrikendpunkt verschrottet, den die Beispiel-App verfügbar macht. Fügen Sie den hervorgehobenen Text im Abschnitt scrape_configs hinzu:
# 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: 'OpenTelemetryTest'
scrape_interval: 1s # poll very quickly for a more responsive demo
static_configs:
- targets: ['localhost:9184']
Prometheus starten
Laden Sie die Konfiguration neu, oder starten Sie den Prometheus-Server neu.
Vergewissern Sie sich, dass OpenTelemetryTest sich im UP-Zustand auf der Seite "Statusziele>" des Prometheus-Webportals befindet.
Geben Sie auf der Seite „Graph“ des Prometheus-Webportals
hatsin das Ausdruckstextfeld ein, und wählen Siehats_sold_Hats
aus. Auf der Registerkarte "Graph" zeigt Prometheus den steigenden Wert des von der Beispiel-App ausgegebenen "Hats-sold"-Zählers an.
In der vorherigen Abbildung wird die Diagrammzeit auf 5m festgelegt, was 5 Minuten beträgt.
Wenn der Prometheus-Server die Beispiel-App noch nicht lange abgefragt hat, müssen Sie möglicherweise warten, bis sich genügend Daten ansammeln.
Anzeigen von Metriken auf einem Grafana-Dashboard
Befolgen Sie die Standardanweisungen , um Grafana zu installieren und mit einer Prometheus-Datenquelle zu verbinden.
Erstellen Sie ein Grafana-Dashboard, indem Sie auf der linken Symbolleiste im Grafana-Webportal auf das + Symbol klicken, und wählen Sie dann "Dashboard" aus. Geben Sie im angezeigten Dashboard-Editor Hats Sold/Sec in das Eingabefeld Titel und rate(hats_sold[5m]) in das Feld „PromQL-Ausdruck“ ein:
Klicken Sie auf Übernehmen, um das neue Dashboard zu speichern und anzuzeigen.
]
Erstellen eines benutzerdefinierten Sammlungstools mithilfe der .NET MeterListener-API
Die .NET-MeterListener-API ermöglicht Ihnen das Erstellen von benutzerdefinierter In-Prozess-Logik, um die von System.Diagnostics.Metrics.Meter aufgezeichneten Messungen zu beobachten. Anleitungen zum Erstellen benutzerdefinierter Logik, die mit der älteren EventCounters-Instrumentierung kompatibel ist, finden Sie unter EventCounters.
Ändern Sie den Code von Program.cs, um MeterListener zu verwenden.
using System.Diagnostics.Metrics;
class Program
{
static Meter s_meter = new("HatCo.HatStore", "1.0.0");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>(
name: "hats-sold",
unit: "Hats",
description: "The number of hats sold in our store");
static void Main(string[] args)
{
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
meterListener.SetMeasurementEventCallback<int>(OnMeasurementRecorded);
// Start the meterListener, enabling InstrumentPublished callbacks.
meterListener.Start();
var rand = Random.Shared;
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable)
{
//// Simulate hat selling transactions.
Thread.Sleep(rand.Next(100, 2500));
s_hatsSold.Add(rand.Next(0, 1000));
}
}
static void OnMeasurementRecorded<T>(
Instrument instrument,
T measurement,
ReadOnlySpan<KeyValuePair<string, object?>> tags,
object? state)
{
Console.WriteLine($"{instrument.Name} recorded measurement {measurement}");
}
}
Die folgende Ausgabe zeigt das Ergebnis der App mit benutzerdefiniertem Callback bei jeder Messung:
> dotnet run
Press any key to exit
hats-sold recorded measurement 978
hats-sold recorded measurement 775
hats-sold recorded measurement 666
hats-sold recorded measurement 66
hats-sold recorded measurement 914
hats-sold recorded measurement 912
...
Beispielcodeerklärung
Die Codeausschnitte in diesem Abschnitt stammen aus dem vorherigen Beispiel.
Im folgenden hervorgehobenen Code wird eine Instanz der MeterListener erstellt, um Messwerte zu empfangen. Das Schlüsselwort using bewirkt, dass Dispose aufgerufen wird, wenn meterListener aus dem Gültigkeitsbereich fällt.
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
Der folgende hervorgehobene Code konfiguriert, von welchen Instrumenten der Listener Messungen erhält. InstrumentPublished ist eine Stellvertretung, die aufgerufen wird, wenn innerhalb der App ein neues Instrument erstellt wird.
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
Der Delegierte kann das Instrument prüfen, um zu entscheiden, ob er sich anmelden möchte. Der Delegat kann beispielsweise den Namen, die Verbrauchseinheit oder eine andere öffentliche Eigenschaft überprüfen. EnableMeasurementEvents ermöglicht den Empfang von Messungen vom angegebenen Instrument. Code, der einen Verweis auf ein Instrument durch einen anderen Ansatz abruft:
- Wird normalerweise nicht gemacht.
- Kann
EnableMeasurementEvents()jederzeit mit dem Verweis aufrufen.
Der Delegat, der aufgerufen wird, wenn Messungen von einem Instrument empfangen werden, wird durch Aufrufen SetMeasurementEventCallbackkonfiguriert:
meterListener.SetMeasurementEventCallback<int>(OnMeasurementRecorded);
// Start the meterListener, enabling InstrumentPublished callbacks.
meterListener.Start();
var rand = Random.Shared;
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable)
{
//// Simulate hat selling transactions.
Thread.Sleep(rand.Next(100, 2500));
s_hatsSold.Add(rand.Next(0, 1000));
}
}
static void OnMeasurementRecorded<T>(
Instrument instrument,
T measurement,
ReadOnlySpan<KeyValuePair<string, object?>> tags,
object? state)
{
Console.WriteLine($"{instrument.Name} recorded measurement {measurement}");
}
Der generische Parameter steuert, welcher Datentyp der Messung vom Rückruf empfangen wird. Zum Beispiel generiert ein Counter<int>int Messungen, ein Counter<double> generiert double Messungen. Instrumente können mit byte, , short, intlong, float, , doubleund decimal Typen erstellt werden. Es wird empfohlen, einen Rückruf für jeden Datentyp zu registrieren, es sei denn, Sie verfügen über szenariospezifische Kenntnisse, dass nicht alle Datentypen erforderlich sind. Wiederholte Aufrufe von SetMeasurementEventCallback mit verschiedenen generischen Argumenten können ein wenig ungewöhnlich erscheinen. Die API wurde so konzipiert, dass MeterListener Messungen mit geringem Leistungsaufwand empfangen kann, in der Regel in nur wenigen Nanosekunden.
Wenn MeterListener.EnableMeasurementEvents aufgerufen wird, kann ein state-Objekt als ein Parameter bereitgestellt werden. Das state Objekt ist beliebig. Wenn Sie in diesem Aufruf ein Statusobjekt angeben, wird es mit diesem Instrument gespeichert und als state Parameter im Rückruf an Sie zurückgegeben. Dies dient sowohl als Komfort als auch als Leistungsoptimierung. Häufig müssen Hörer Folgendes ausführen:
- Erstellen Sie ein Objekt für jedes Instrument, das Messungen im Speicher speichert.
- Verwenden Sie Code, um Berechnungen für diese Messungen durchzuführen.
Erstellen Sie alternativ ein Dictionary, das vom Instrument zum Speicherobjekt zugeordnet wird, und sehen Sie darin bei jeder Messung nach. Die Verwendung eines Dictionary ist viel langsamer als der Zugriff darauf von state.
meterListener.Start();
Der vorangehende Code startet MeterListener, wodurch Rückrufe aktiviert werden. Der Delegat InstrumentPublished wird für jedes bereits vorhandene Instrument im Prozess aufgerufen. Neu erstellte Instrument-Objekte führen ebenfalls dazu, dass InstrumentPublished aufgerufen wird.
using MeterListener meterListener = new MeterListener();
Wenn die App mit dem Lauschen fertig ist, beendet das Verwerfen des Listeners den Ablauf von Rückrufen und gibt alle internen Verweise auf das Listenerobjekt frei. Das Schlüsselwort using, das beim Deklarieren von meterListener verwendet wird, bewirkt, dass Dispose aufgerufen wird, wenn die Variable den Gültigkeitsbereich verlässt. Beachten Sie, dass Dispose nur verspricht, dass keine neuen Rückrufe initiiert werden. Da Rückrufe in verschiedenen Threads auftreten, werden möglicherweise noch Rückrufe ausgeführt, nachdem der Aufruf an Dispose zurückgegeben wurde.
Um sicherzustellen, dass ein bestimmter Codebereich im Rückruf derzeit nicht ausgeführt wird und in Zukunft nicht ausgeführt wird, muss die Threadsynchronisierung hinzugefügt werden.
Dispose enthält standardmäßig keine Synchronisierung, da:
- Die Synchronisierung erhöht den Leistungsaufwand in jedem Messrückruf.
-
MeterListenerist als hochleistungsbewusste API konzipiert.