Samla in mått
Den här artikeln gäller för: ✔️ .NET Core 3.1 och senare ✔️ .NET Framework 4.6.1 och senare
Instrumenterad kod kan registrera numeriska mått, men måtten måste vanligtvis aggregeras, överföras och lagras för att skapa användbara mått för övervakning. Processen för att aggregera, överföra och lagra data kallas insamling. Den här självstudien visar flera exempel på insamling av mått:
- Fylla i mått i Grafana med OpenTelemetry och Prometheus.
- Visa mått i realtid med
dotnet-counters
- Skapa ett anpassat samlingsverktyg med hjälp av det underliggande .NET-API MeterListener :et.
Mer information om instrumentation och alternativ för anpassade mått finns i Jämför mått-API:er.
Förutsättningar
- .NET Core 3.1 SDK eller senare
Skapa en exempelapp
Innan mått kan samlas in måste mätningarna skapas. Den här självstudien skapar en app som har grundläggande måttinstrumentation. .NET-körningen har också olika inbyggda mått. Mer information om hur du skapar nya mått med hjälp av API:et System.Diagnostics.Metrics.Meter finns i självstudiekursen för instrumentering.
dotnet new console -o metric-instr
cd metric-instr
dotnet add package System.Diagnostics.DiagnosticSource
Ersätt innehållet i Program.cs
med följande kod:
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));
}
}
}
Föregående kod simulerar säljhattar med slumpmässiga intervall och slumpmässiga tider.
Visa mått med dotnet-counters
dotnet-counters är ett kommandoradsverktyg som kan visa livemått för .NET Core-appar på begäran. Det kräver inte konfiguration, vilket gör det användbart för ad hoc-undersökningar eller för att verifiera att måttinstrumentation fungerar. Den fungerar med både System.Diagnostics.Metrics baserade API:er och EventCounters.
Om verktyget dotnet-counters inte är installerat kör du följande kommando:
dotnet tool update -g dotnet-counters
När exempelappen körs startar du dotnet-counters. Följande kommando visar ett exempel på övervakning av dotnet-counters
alla mått från mätaren HatCo.HatStore
. Mätarnamnet är skiftlägeskänsligt. Vår exempelapp var metric-instr.exe, ersätt den med namnet på exempelappen.
dotnet-counters monitor -n metric-instr HatCo.HatStore
Utdata som ser ut ungefär så här visas:
Press p to pause, r to resume, q to quit.
Status: Running
[HatCo.HatStore]
hats-sold (Count / 1 sec) 4
dotnet-counters
kan köras med en annan uppsättning mått för att se några av de inbyggda instrumentationerna från .NET-körningen:
dotnet-counters monitor -n metric-instr
Utdata som ser ut ungefär så här visas:
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
Mer information finns i dotnet-counters. Mer information om mått i .NET finns i inbyggda mått.
Visa mått i Grafana med OpenTelemetry och Prometheus
Översikt
- Är ett leverantörsneutralt projekt med öppen källkod som stöds av Cloud Native Computing Foundation.
- Standardiserar generering och insamling av telemetri för molnbaserad programvara.
- Fungerar med .NET med hjälp av .NET-mått-API:er.
- Stöds av Azure Monitor och många APM-leverantörer.
Den här självstudien visar en av de integreringar som är tillgängliga för OpenTelemetry-mått med hjälp av OSS Prometheus - och Grafana-projekten . Dataflödet för mått:
.NET-mått-API:erna registrerar mått från exempelappen.
OpenTelemetry-biblioteket som körs i appen aggregerar måtten.
Prometheus-exporteringsbiblioteket gör aggregerade data tillgängliga via en HTTP-måttslutpunkt. "Exporter" är vad OpenTelemetry kallar biblioteken som överför telemetri till leverantörsspecifika serverdelar.
En Prometheus-server:
- Avsöker måttslutpunkten
- Läser data
- Lagrar data i en databas för långsiktig beständighet. Prometheus refererar till att läsa och lagra data som att skrapa en slutpunkt.
- Kan köras på en annan dator
Grafana-servern:
- Kör frågor mot data som lagras i Prometheus och visar dem på en webbaserad övervakningsinstrumentpanel.
- Kan köras på en annan dator.
Konfigurera exempelappen så att den använder Prometheus-exportören i OpenTelemetry
Lägg till en referens till OpenTelemetry Prometheus-exportören i exempelappen:
dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener --prerelease
Kommentar
I den här självstudien används en förhandsversion av OpenTelemetrys Prometheus-stöd som är tillgängligt i skrivande stund.
Uppdatera Program.cs
med 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));
}
}
}
I koden ovan:
AddMeter("HatCo.HatStore")
konfigurerar OpenTelemetry för att överföra alla mått som samlas in av mätaren som definieras i appen.AddPrometheusHttpListener
konfigurerar OpenTelemetry för att:- Exponera Prometheus måttslutpunkt på port
9184
- Använd HttpListener.
- Exponera Prometheus måttslutpunkt på port
Mer information om konfigurationsalternativ för OpenTelemetry finns i OpenTelemetry-dokumentationen . Dokumentationen om OpenTelemetry visar värdalternativ för ASP.NET appar.
Kör appen och låt den köras så att mätningar kan samlas in:
dotnet run
Konfigurera Prometheus
Följ de första stegen i Prometheus för att konfigurera en Prometheus-server och bekräfta att den fungerar.
Ändra konfigurationsfilen prometheus.yml så att Prometheus skrapar måttslutpunkten som exempelappen exponerar. Lägg till följande markerade text i avsnittet scrape_configs
:
# 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']
Starta Prometheus
Ladda om konfigurationen eller starta om Prometheus-servern.
Bekräfta att OpenTelemetryTest är i UP-tillståndet på sidan Statusmål> i Prometheus-webbportalen.
På sidan Graph i Prometheus-webbportalen anger du
hats
i textrutan uttryck och väljerhats_sold_Hats
På diagramfliken visar Prometheus det ökande värdet för den "hattsålda" räknaren som genereras av exempelappen.
I föregående bild är graftiden inställd på 5 m, vilket är 5 minuter.
Om Prometheus-servern inte har skrapat exempelappen länge kan du behöva vänta tills data ackumuleras.
Visa mått på en Grafana-instrumentpanel
Följ standardanvisningarna för att installera Grafana och ansluta det till en Prometheus-datakälla.
Skapa en Grafana-instrumentpanel genom att + klicka på ikonen i det vänstra verktygsfältet i Grafana-webbportalen och välj sedan Instrumentpanel. I instrumentpanelsredigeraren som visas anger du Hats Sold/Sec i rutan Rubrikinmatning och rate(hats_sold[5m]) i fältet PromQL-uttryck:
Klicka på Använd för att spara och visa den nya instrumentpanelen.
]
Skapa ett anpassat samlingsverktyg med .NET MeterListener-API:et
Med .NET-API MeterListener :et kan du skapa anpassad processlogik för att observera de mätningar som registreras av System.Diagnostics.Metrics.Meter. Vägledning om hur du skapar anpassad logik som är kompatibel med den äldre EventCounters-instrumentationen finns i EventCounters.
Ändra koden Program.cs
för att använda 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}");
}
}
Följande utdata visar utdata från appen med anpassat återanrop för varje mätning:
> 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
...
Förklara exempelkoden
Kodfragmenten i det här avsnittet kommer från föregående exempel.
I följande markerade kod skapas en instans av den MeterListener för att ta emot mått. Nyckelordet using
anropas Dispose
när omfånget går utanför omfånget meterListener
.
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
Följande markerade kod konfigurerar vilka instrument lyssnaren tar emot mått från. InstrumentPublished är ett ombud som anropas när ett nytt instrument skapas i appen.
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
Ombudet kan undersöka instrumentet för att avgöra om det ska prenumerera. Ombudet kan till exempel kontrollera namnet, mätaren eller någon annan offentlig egenskap. EnableMeasurementEvents gör det möjligt att ta emot mätningar från det angivna instrumentet. Kod som hämtar en referens till ett instrument med en annan metod:
- Görs vanligtvis inte.
- Kan anropa
EnableMeasurementEvents()
när som helst med referensen.
Ombudet som anropas när mätningar tas emot från ett instrument konfigureras genom att anropa SetMeasurementEventCallback:
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}");
}
Den allmänna parametern styr vilken datatyp av mätning som tas emot av återanropet. Till exempel genererar int
en Counter<int>
mätning, Counter<double>
genererar double
mätningar. Instrument kan skapas med byte
typerna , short
, int
, long
, float
och double
decimal
. Vi rekommenderar att du registrerar ett återanrop för varje datatyp om du inte har scenariospecifik kunskap om att inte alla datatyper behövs. Att göra upprepade anrop till SetMeasurementEventCallback
med olika generiska argument kan verka lite ovanligt. API:et utformades på det här sättet så att en MeterListener
kan ta emot mätningar med låg prestanda, vanligtvis bara några få nanosekunder.
När MeterListener.EnableMeasurementEvents
anropas kan ett state
objekt anges som en av parametrarna. Objektet state
är godtyckligt. Om du anger ett tillståndsobjekt i det anropet lagras det med det instrumentet och returneras till dig som state
parameter i återanropet. Detta är avsett både som en bekvämlighet och som en prestandaoptimering. Lyssnare behöver ofta:
- Skapa ett objekt för varje instrument som lagrar mått i minnet.
- Ha kod för att göra beräkningar på dessa mätningar.
Du kan också skapa en Dictionary
som mappar från instrumentet till lagringsobjektet och letar upp den vid varje mätning. Att använda en Dictionary
är mycket långsammare än att komma åt det från state
.
meterListener.Start();
Föregående kod startar som MeterListener
aktiverar återanrop. Ombudet InstrumentPublished
anropas för varje befintligt instrument i processen. Nyligen skapade instrumentobjekt utlöses InstrumentPublished
också för att anropas.
using MeterListener meterListener = new MeterListener();
När appen är klar med att lyssna stoppar disponeringen av lyssnaren flödet av återanrop och släpper eventuella interna referenser till lyssnarobjektet. Nyckelordet using
som används när du deklarerar meterListener
orsakar Dispose
att anropas när variabeln hamnar utanför omfånget. Observera att Dispose
det bara är lovande att det inte initierar nya återanrop. Eftersom återanrop sker i olika trådar kan det fortfarande finnas motringningar som pågår efter att anropet till Dispose
returnerar.
För att garantera att en viss kodregion i återanropet inte körs och inte körs i framtiden måste trådsynkronisering läggas till. Dispose
inkluderar inte synkronisering som standard eftersom:
- Synkronisering lägger till prestandakostnader i varje återanrop för mätning.
MeterListener
är utformat som ett mycket prestandamedvetet API.