Metrische gegevens verzamelen
Dit artikel is van toepassing op: ✔️ .NET Core 3.1 en hoger ✔️ .NET Framework 4.6.1 en hoger
Met instrumenteerde code kunnen numerieke metingen worden vastgelegd, maar de metingen moeten meestal worden geaggregeerd, verzonden en opgeslagen om nuttige metrische gegevens te maken voor bewaking. Het proces voor het samenvoegen, verzenden en opslaan van gegevens wordt verzameling genoemd. In deze zelfstudie ziet u verschillende voorbeelden van het verzamelen van metrische gegevens:
- Metrische gegevens in Grafana vullen met OpenTelemetry en Prometheus.
- Metrische gegevens in realtime weergeven met
dotnet-counters
- Een aangepast verzamelingsprogramma maken met behulp van de onderliggende .NET-API MeterListener .
Zie Metrische API's vergelijken voor meer informatie over aangepaste metrische instrumentatie en opties.
Vereisten
- .NET Core 3.1 SDK of hoger
Een voorbeeld-app maken
Voordat metrische gegevens kunnen worden verzameld, moeten metingen worden geproduceerd. In deze zelfstudie maakt u een app met eenvoudige metrische instrumentatie. De .NET-runtime heeft ook verschillende ingebouwde metrische gegevens. Zie de zelfstudie voor instrumentatie voor meer informatie over het maken van nieuwe metrische gegevens met behulp van de System.Diagnostics.Metrics.Meter API.
dotnet new console -o metric-instr
cd metric-instr
dotnet add package System.Diagnostics.DiagnosticSource
Vervang de inhoud door Program.cs
de volgende 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));
}
}
}
De voorgaande code simuleert het verkopen van hoeden met willekeurige intervallen en willekeurige tijden.
Metrische gegevens weergeven met dotnet-tellers
dotnet-counters is een opdrachtregelprogramma waarmee live metrische gegevens voor .NET Core-apps op aanvraag kunnen worden weergegeven. Hiervoor is geen installatie vereist, waardoor het nuttig is voor ad-hoconderzoeken of om te controleren of metrische instrumentatie werkt. Het werkt met zowel System.Diagnostics.Metrics op gebaseerde API's als EventCounters.
Als het hulpprogramma dotnet-counters niet is geïnstalleerd, voert u de volgende opdracht uit:
dotnet tool update -g dotnet-counters
Terwijl de voorbeeld-app wordt uitgevoerd, start u dotnet-tellers. De volgende opdracht toont een voorbeeld van het bewaken van dotnet-counters
alle metrische gegevens van de HatCo.HatStore
meter. De naam van de meter is hoofdlettergevoelig. Onze voorbeeld-app is metric-instr.exe. Vervang deze door de naam van uw voorbeeld-app.
dotnet-counters monitor -n metric-instr HatCo.HatStore
Er wordt een venster weergegeven met ongeveer deze uitvoer:
Press p to pause, r to resume, q to quit.
Status: Running
[HatCo.HatStore]
hats-sold (Count / 1 sec) 4
dotnet-counters
kan worden uitgevoerd met een andere set metrische gegevens om enkele van de ingebouwde instrumentatie van de .NET-runtime te bekijken:
dotnet-counters monitor -n metric-instr
Er wordt een venster weergegeven met ongeveer deze uitvoer:
Press p to pause, r to resume, q to quit.
Status: Running
[System.Runtime]
% Time in GC since last GC (%) 0
Allocation Rate (B / 1 sec) 8,168
CPU Usage (%) 0
Exception Count (Count / 1 sec) 0
GC Heap Size (MB) 2
Gen 0 GC Count (Count / 1 sec) 0
Gen 0 Size (B) 2,216,256
Gen 1 GC Count (Count / 1 sec) 0
Gen 1 Size (B) 423,392
Gen 2 GC Count (Count / 1 sec) 0
Gen 2 Size (B) 203,248
LOH Size (B) 933,216
Monitor Lock Contention Count (Count / 1 sec) 0
Number of Active Timers 1
Number of Assemblies Loaded 39
ThreadPool Completed Work Item Count (Count / 1 sec) 0
ThreadPool Queue Length 0
ThreadPool Thread Count 3
Working Set (MB) 30
Zie dotnet-tellers voor meer informatie. Zie ingebouwde metrische gegevens voor meer informatie over metrische gegevens in .NET.
Metrische gegevens weergeven in Grafana met OpenTelemetry en Prometheus
Overzicht
- Is een leverancierneutraal opensource-project dat wordt ondersteund door de Cloud Native Computing Foundation.
- Standaardiseert het genereren en verzamelen van telemetrie voor cloudeigen software.
- Werkt met .NET met behulp van de metrische .NET-API's.
- Wordt goedgekeurd door Azure Monitor en veel APM-leveranciers.
In deze zelfstudie ziet u een van de integraties die beschikbaar zijn voor metrische gegevens van OpenTelemetry met behulp van de OSS Prometheus - en Grafana-projecten . De metrische gegevensstroom:
De metrische .NET-API's registreren metingen uit de voorbeeld-app.
De OpenTelemetry-bibliotheek die in de app wordt uitgevoerd, voegt de metingen samen.
De Prometheus-exportbibliotheek maakt de geaggregeerde gegevens beschikbaar via een eindpunt voor metrische HTTP-gegevens. 'Exporteur' is wat OpenTelemetry de bibliotheken aanroept die telemetrie verzenden naar leverancierspecifieke back-ends.
Een Prometheus-server:
- Peilt het eindpunt voor metrische gegevens
- De gegevens lezen
- Slaat de gegevens op in een database voor persistentie op lange termijn. Prometheus verwijst naar het lezen en opslaan van gegevens als het scrapen van een eindpunt.
- Kan worden uitgevoerd op een andere computer
De Grafana-server:
- Query's uitvoeren op de gegevens die zijn opgeslagen in Prometheus en deze weergeven op een bewakingsdashboard op internet.
- Kan worden uitgevoerd op een andere computer.
De voorbeeld-app configureren voor het gebruik van de Prometheus-exporteur van OpenTelemetry
Voeg een verwijzing naar de OpenTelemetry Prometheus-exporteur toe aan de voorbeeld-app:
dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener --prerelease
Notitie
In deze zelfstudie wordt gebruikgemaakt van een voorlopige build van de Prometheus-ondersteuning van OpenTelemetry die beschikbaar is op het moment van schrijven.
Bijwerken Program.cs
met OpenTelemetry-configuratie:
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));
}
}
}
In de voorgaande code:
AddMeter("HatCo.HatStore")
configureert OpenTelemetry om alle metrische gegevens te verzenden die zijn verzameld door de meter die in de app is gedefinieerd.AddPrometheusHttpListener
configureert OpenTelemetry voor:- Eindpunt voor metrische gegevens van Prometheus beschikbaar maken op poort
9184
- Gebruik httplistener.
- Eindpunt voor metrische gegevens van Prometheus beschikbaar maken op poort
Zie de documentatie over OpenTelemetry voor meer informatie over configuratieopties voor OpenTelemetry. De OpenTelemetry-documentatie bevat hostingopties voor ASP.NET-apps.
Voer de app uit en laat deze actief zodat metingen kunnen worden verzameld:
dotnet run
Prometheus instellen en configureren
Volg de eerste stappen van Prometheus om een Prometheus-server in te stellen en te bevestigen dat deze werkt.
Wijzig het prometheus.yml-configuratiebestand zodat Prometheus het eindpunt voor metrische gegevens verwijdert dat de voorbeeld-app weergeeft. Voeg de volgende gemarkeerde tekst toe in de scrape_configs
sectie:
# 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
Laad de configuratie opnieuw of start de Prometheus-server opnieuw op.
Controleer of OpenTelemetryTest de UP-status heeft op de pagina Statusdoelen> van de Prometheus-webportal.
Voer
hats
op de grafiekpagina van de Prometheus-webportal het tekstvak voor de expressie in en selecteerhats_sold_Hats
In het grafiektabblad geeft Prometheus de toenemende waarde weer van het teller 'hats-sold' dat door de voorbeeld-app wordt verzonden.
In de voorgaande afbeelding wordt de grafiektijd ingesteld op 5 min. Dit is 5 minuten.
Als de Prometheus-server de voorbeeld-app lang niet heeft gesloopt, moet u mogelijk wachten totdat gegevens zijn verzameld.
Metrische gegevens weergeven op een Grafana-dashboard
Volg de standaardinstructies om Grafana te installeren en deze te verbinden met een Prometheus-gegevensbron.
Maak een Grafana-dashboard door op het + pictogram op de linkerwerkbalk in de Grafana-webportal te klikken en vervolgens Dashboard te selecteren. Voer in de dashboardeditor die wordt weergegeven Hats Sold/Sec in het invoervak Titel en rate (hats_sold[5m]) in het veld PromQL-expressie in:
Klik op Toepassen om het nieuwe dashboard op te slaan en weer te geven.
]
Een aangepast verzamelingsprogramma maken met behulp van de .NET MeterListener-API
Met de .NET-API MeterListener kunt u aangepaste in-processlogica maken om de metingen te observeren die worden vastgelegd door System.Diagnostics.Metrics.Meter. Zie EventCounters voor hulp bij het maken van aangepaste logica die compatibel is met de oudere EventCounters-instrumentatie.
Wijzig de code voor Program.cs
gebruik MeterListener:
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}");
}
}
In de volgende uitvoer ziet u de uitvoer van de app met aangepaste callback voor elke meting:
> 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
...
Uitleg van de voorbeeldcode
De codefragmenten in deze sectie zijn afkomstig uit het voorgaande voorbeeld.
In de volgende gemarkeerde code wordt een exemplaar van de MeterListener code gemaakt om metingen te ontvangen. Het using
trefwoord wordt Dispose
aangeroepen wanneer het meterListener
bereik wordt bereikt.
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
Met de volgende gemarkeerde code wordt geconfigureerd van welke instrumenten de listener metingen ontvangt. InstrumentPublished is een gemachtigde die wordt aangeroepen wanneer een nieuw instrument in de app wordt gemaakt.
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
De gedelegeerde kan het instrument onderzoeken om te bepalen of hij zich moet abonneren. De gedelegeerde kan bijvoorbeeld de naam, de meter of een andere openbare eigenschap controleren. EnableMeasurementEvents maakt het ontvangen van metingen van het opgegeven instrument mogelijk. Code waarmee een verwijzing naar een instrument wordt verkregen door een andere benadering:
- Dit gebeurt meestal niet.
- Kan op elk gewenst moment aanroepen
EnableMeasurementEvents()
met de verwijzing.
De gemachtigde die wordt aangeroepen wanneer metingen van een instrument worden ontvangen, wordt geconfigureerd door het aanroepen SetMeasurementEventCallbackvan:
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}");
}
De algemene parameter bepaalt welk gegevenstype meting wordt ontvangen door de callback. Een meting genereert int
bijvoorbeeld Counter<int>
metingen, Counter<double>
genereert double
metingen. Instrumenten kunnen worden gemaakt met byte
, , short
, int
, long
, , float
en decimal
double
typen. We raden u aan een callback te registreren voor elk gegevenstype, tenzij u scenariospecifieke kennis hebt dat niet alle gegevenstypen nodig zijn. Het maken van herhaalde aanroepen SetMeasurementEventCallback
met verschillende algemene argumenten kan een beetje ongebruikelijk lijken. De API is op deze manier ontworpen om metingen MeterListener
te ontvangen met lage overheadprestaties, meestal slechts een paar nanoseconden.
Wanneer MeterListener.EnableMeasurementEvents
wordt aangeroepen, kan een state
object worden opgegeven als een van de parameters. Het state
object is willekeurig. Als u een statusobject in die aanroep opgeeft, wordt het met dat instrument opgeslagen en naar u geretourneerd als de state
parameter in de callback. Dit is zowel bedoeld als als een optimalisatie van prestaties. Vaak moeten listeners het volgende doen:
- Maak een object voor elk instrument dat metingen in het geheugen opslaat.
- Code hebben om berekeningen uit te voeren voor deze metingen.
U kunt ook een Dictionary
object maken dat van het instrument wordt toegewezen aan het opslagobject en het opzoekt op elke meting. Het gebruik van een Dictionary
is veel langzamer dan het openen van state
.
meterListener.Start();
Met de voorgaande code wordt callbacks MeterListener
gestart. De InstrumentPublished
gemachtigde wordt aangeroepen voor elk bestaand instrument in het proces. Nieuw gemaakte instrumentobjecten worden ook geactiveerd InstrumentPublished
om aan te roepen.
using MeterListener meterListener = new MeterListener();
Wanneer de app klaar is met luisteren, stopt het verwijderen van de listener de stroom van callbacks en worden eventuele interne verwijzingen naar het listenerobject vrijgegeven. Het using
trefwoord dat wordt gebruikt bij het declareren meterListener
, wordt Dispose
aangeroepen wanneer de variabele buiten het bereik valt. Houd er rekening mee dat het Dispose
alleen belooft dat er geen nieuwe callbacks worden gestart. Omdat callbacks plaatsvinden op verschillende threads, kunnen er nog steeds callbacks worden uitgevoerd nadat de aanroep die moet Dispose
worden geretourneerd.
Om ervoor te zorgen dat een bepaalde coderegio in de callback momenteel niet wordt uitgevoerd en in de toekomst niet wordt uitgevoerd, moet threadsynchronisatie worden toegevoegd. Dispose
bevat standaard synchronisatie niet omdat:
- Synchronisatie voegt prestatieoverhead toe aan elke callback voor metingen.
MeterListener
is ontworpen als een zeer prestatiebewuste API.