Zbieranie metryk
Ten artykuł dotyczy: ✔️ .NET Core 3.1 i nowsze ✔️ wersje .NET Framework 4.6.1 i nowsze
Instrumentowany kod może rejestrować miary liczbowe, ale pomiary zwykle muszą być agregowane, przesyłane i przechowywane w celu utworzenia przydatnych metryk do monitorowania. Proces agregowania, przesyłania i przechowywania danych jest nazywany kolekcją. W tym samouczku przedstawiono kilka przykładów zbierania metryk:
- Wypełnianie metryk w narzędziu Grafana za pomocą funkcji OpenTelemetry i Prometheus.
- Wyświetlanie metryk w czasie rzeczywistym za pomocą polecenia
dotnet-counters
- Tworzenie niestandardowego narzędzia do zbierania przy użyciu bazowego interfejsu API platformy .NET MeterListener .
Aby uzyskać więcej informacji na temat instrumentacji i opcji metryk niestandardowych, zobacz Porównanie interfejsów API metryk.
Wymagania wstępne
- Zestaw .NET Core 3.1 SDK lub nowszy
Tworzenie przykładowej aplikacji
Aby można było zbierać metryki, należy wygenerować pomiary. W tym samouczku zostanie utworzona aplikacja z podstawową instrumentacją metryk. Środowisko uruchomieniowe platformy .NET ma również różne wbudowane metryki. Aby uzyskać więcej informacji na temat tworzenia nowych metryk przy użyciu interfejsu System.Diagnostics.Metrics.Meter API, zobacz samouczek instrumentacji.
dotnet new console -o metric-instr
cd metric-instr
dotnet add package System.Diagnostics.DiagnosticSource
Zastąp zawartość Program.cs
następującym kodem:
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));
}
}
}
Powyższy kod symuluje sprzedaż kapeluszy w losowych odstępach czasu i losowych.
Wyświetlanie metryk za pomocą liczników dotnet-counter
dotnet-counters to narzędzie wiersza polecenia, które może wyświetlać metryki na żywo dla aplikacji platformy .NET Core na żądanie. Nie wymaga konfiguracji, co ułatwia badanie ad hoc lub sprawdzanie, czy instrumentacja metryk działa. Działa zarówno z interfejsami API opartymi na usłudze, jak System.Diagnostics.Metrics i z usługą EventCounters.
Jeśli narzędzie dotnet-counters nie jest zainstalowane, uruchom następujące polecenie:
dotnet tool update -g dotnet-counters
Gdy przykładowa aplikacja jest uruchomiona, uruchom liczniki dotnet-counter. Poniższe polecenie przedstawia przykład dotnet-counters
monitorowania wszystkich metryk z miernika HatCo.HatStore
. W nazwie miernika jest uwzględniana wielkość liter. Nasza przykładowa aplikacja to metric-instr.exe. Zastąp to nazwą przykładowej aplikacji.
dotnet-counters monitor -n metric-instr HatCo.HatStore
Wyświetlane są dane wyjściowe podobne do następujących:
Press p to pause, r to resume, q to quit.
Status: Running
[HatCo.HatStore]
hats-sold (Count / 1 sec) 4
dotnet-counters
Można uruchomić za pomocą innego zestawu metryk, aby wyświetlić niektóre wbudowane instrumentacje ze środowiska uruchomieniowego platformy .NET:
dotnet-counters monitor -n metric-instr
Wyświetlane są dane wyjściowe podobne do następujących:
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
Aby uzyskać więcej informacji, zobacz dotnet-counters. Aby dowiedzieć się więcej o metrykach na platformie .NET, zobacz wbudowane metryki.
Wyświetlanie metryk w narzędziu Grafana przy użyciu rozwiązań OpenTelemetry i Prometheus
Omówienie
- Jest neutralnym dla dostawcy projektem open source obsługiwanym przez Cloud Native Computing Foundation.
- Standaryzacja generowania i zbierania danych telemetrycznych dla oprogramowania natywnego dla chmury.
- Współpracuje z platformą .NET przy użyciu interfejsów API metryk platformy .NET.
- Jest zatwierdzony przez usługę Azure Monitor i wielu dostawców APM.
W tym samouczku przedstawiono jedną z integracji dostępnych dla metryk OpenTelemetry przy użyciu projektów Prometheus i Grafana systemu operacyjnego. Przepływ danych metryk:
Interfejsy API metryk platformy .NET rejestrują pomiary z przykładowej aplikacji.
Biblioteka OpenTelemetry uruchomiona w aplikacji agreguje miary.
Biblioteka eksportera Prometheus udostępnia zagregowane dane za pośrednictwem punktu końcowego metryk HTTP. Funkcja "Eksporter" wywołuje biblioteki, które przesyłają dane telemetryczne do zapleczy specyficznych dla dostawcy.
Serwer Prometheus:
- Sonduje punkt końcowy metryk
- Odczytuje dane
- Przechowuje dane w bazie danych pod kątem trwałości długoterminowej. Prometheus odnosi się do odczytywania i przechowywania danych jako złomowania punktu końcowego.
- Może działać na innej maszynie
Serwer Grafana:
- Wykonuje zapytanie dotyczące danych przechowywanych w rozwiązaniu Prometheus i wyświetla je na internetowym pulpicie nawigacyjnym monitorowania.
- Można uruchomić na innej maszynie.
Konfigurowanie przykładowej aplikacji do korzystania z eksportera Prometheus firmy OpenTelemetry
Dodaj odwołanie do eksportera OpenTelemetry Prometheus do przykładowej aplikacji:
dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener --prerelease
Uwaga
W tym samouczku jest używana kompilacja wstępna obsługi rozwiązania Prometheus firmy OpenTelemetry dostępna w momencie pisania tekstu.
Zaktualizuj Program.cs
za pomocą konfiguracji OpenTelemetry:
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));
}
}
}
Powyższy kod:
AddMeter("HatCo.HatStore")
Konfiguruje metodę OpenTelemetry w celu przesyłania wszystkich metryk zebranych przez miernik zdefiniowany w aplikacji.AddPrometheusHttpListener
Konfiguruje bibliotekę OpenTelemetry w taki sposób, aby:- Uwidaczniaj punkt końcowy metryk rozwiązania Prometheus na porcie
9184
- Użyj elementu HttpListener.
- Uwidaczniaj punkt końcowy metryk rozwiązania Prometheus na porcie
Aby uzyskać więcej informacji na temat opcji konfiguracji OpenTelemetry, zobacz dokumentację platformy OpenTelemetry. W dokumentacji platformy OpenTelemetry przedstawiono opcje hostingu dla aplikacji ASP.NET.
Uruchom aplikację i pozostaw ją uruchomioną, aby można było zbierać pomiary:
dotnet run
Konfigurowanie i konfigurowanie rozwiązania Prometheus
Wykonaj pierwsze kroki rozwiązania Prometheus, aby skonfigurować serwer Prometheus i upewnij się, że działa.
Zmodyfikuj plik konfiguracji prometheus.yml , aby prometheus zeskrobał punkt końcowy metryk, który ujawnia przykładowa aplikacja. Dodaj następujący wyróżniony tekst w scrape_configs
sekcji:
# 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']
Rozpocznij prometheus
Załaduj ponownie konfigurację lub uruchom ponownie serwer Prometheus.
Upewnij się, że plik OpenTelemetryTest jest w stanie UP na stronie Cele stanu>w portalu internetowym Prometheus.
Na stronie Graf portalu internetowego Prometheus wprowadź
hats
w polu tekstowym wyrażenie i wybierz pozycjęhats_sold_Hats
Na karcie grafu Prometheus pokazuje rosnącą wartość licznika "hats-sold" emitowanego przez przykładową aplikację.
Na powyższym obrazie czas grafu jest ustawiony na 5 m, czyli 5 minut.
Jeśli serwer Prometheus nie od dawna złomuje przykładowej aplikacji, może być konieczne poczekanie na zebranie danych.
Wyświetlanie metryk na pulpicie nawigacyjnym narzędzia Grafana
Postępuj zgodnie ze standardowymi instrukcjami , aby zainstalować aplikację Grafana i połączyć ją ze źródłem danych Prometheus.
Utwórz pulpit nawigacyjny narzędzia Grafana, klikając ikonę + na lewym pasku narzędzi w portalu internetowym Grafana, a następnie wybierz pozycję Pulpit nawigacyjny. W wyświetlonym edytorze pulpitu nawigacyjnego wprowadź Hats Sold/Sec w polu Wprowadzanie tytułu i szybkość (hats_sold[5m]) w polu wyrażenia PromQL:
Kliknij przycisk Zastosuj , aby zapisać i wyświetlić nowy pulpit nawigacyjny.
]
Tworzenie niestandardowego narzędzia do zbierania przy użyciu interfejsu API miernika platformy .NET
Interfejs API platformy .NET MeterListener umożliwia tworzenie niestandardowej logiki przetwarzania w celu obserwowania pomiarów rejestrowanych przez System.Diagnostics.Metrics.Meterprogram . Aby uzyskać wskazówki dotyczące tworzenia logiki niestandardowej zgodnej ze starszą instrumentacją EventCounters, zobacz EventCounters.
Zmodyfikuj kod , Program.cs
aby użyć polecenia 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}");
}
}
Następujące dane wyjściowe pokazują dane wyjściowe aplikacji z niestandardowym wywołaniem zwrotnym dla każdej miary:
> 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
...
Wyjaśnienie przykładowego kodu
Fragmenty kodu w tej sekcji pochodzą z poprzedniego przykładu.
W poniższym wyróżnionym kodzie tworzone jest wystąpienie obiektu w celu odbierania MeterListener pomiarów. Słowo using
kluczowe powoduje Dispose
wywołanie, gdy meterListener
element wykracza poza zakres.
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
Poniższy wyróżniony kod konfiguruje instrumenty, z których odbiornik odbiera pomiary. InstrumentPublished to delegat wywoływany podczas tworzenia nowego instrumentu w aplikacji.
using MeterListener meterListener = new();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name is "HatCo.HatStore")
{
listener.EnableMeasurementEvents(instrument);
}
};
Delegat może zbadać instrument, aby zdecydować, czy zasubskrybować. Na przykład delegat może sprawdzić nazwę, miernik lub dowolną inną właściwość publiczną. EnableMeasurementEvents umożliwia odbieranie pomiarów z określonego instrumentu. Kod, który uzyskuje odwołanie do instrumentu przy użyciu innego podejścia:
- Zazwyczaj nie jest wykonywane.
- W dowolnym momencie można wywołać
EnableMeasurementEvents()
odwołanie.
Delegat wywoływany podczas odbierania pomiarów z instrumentu jest konfigurowany przez wywołanie metody 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}");
}
Ogólny parametr określa typ danych pomiaru odbierany przez wywołanie zwrotne. Na przykład generuje Counter<int>
miary int
, Counter<double>
generuje miary double
. Instrumenty można tworzyć przy użyciu byte
typów , , long
float
double
short
int
, i .decimal
Zalecamy zarejestrowanie wywołania zwrotnego dla każdego typu danych, chyba że masz wiedzę specyficzną dla scenariusza, że nie są potrzebne wszystkie typy danych. Wykonywanie powtarzających się wywołań z SetMeasurementEventCallback
różnymi argumentami ogólnymi może wydawać się nieco nietypowe. Interfejs API został zaprojektowany w ten sposób, aby umożliwić MeterListener
odbieranie pomiarów z niskim obciążeniem wydajności, zwykle tylko kilka nanosekund.
Po MeterListener.EnableMeasurementEvents
wywołaniu state
obiekt można podać jako jeden z parametrów. Obiekt state
jest dowolny. Jeśli podasz obiekt stanu w tym wywołaniu, jest on przechowywany z tym instrumentem i zwracany jako state
parametr w wywołaniu zwrotnym. Jest to przeznaczone zarówno jako wygoda, jak i optymalizacja wydajności. Często odbiorniki muszą:
- Utwórz obiekt dla każdego instrumentu, który przechowuje miary w pamięci.
- Należy wykonać obliczenia dotyczące tych pomiarów za pomocą kodu.
Możesz też utworzyć obiekt Dictionary
mapujący z instrumentu na obiekt magazynu i wyszukać go na każdym pomiarze. Korzystanie z obiektu Dictionary
jest znacznie wolniejsze niż uzyskiwanie do niego dostępu z programu state
.
meterListener.Start();
Powyższy kod uruchamia MeterListener
funkcję , która włącza wywołania zwrotne. Delegat InstrumentPublished
jest wywoływany dla każdego wstępnie istniejącego instrumentu w procesie. Nowo utworzone obiekty instrumentu również są wyzwalane InstrumentPublished
do wywołania.
using MeterListener meterListener = new MeterListener();
Po zakończeniu nasłuchiwania aplikacja powoduje zatrzymanie przepływu wywołań zwrotnych i wydanie wszystkich odwołań wewnętrznych do obiektu odbiornika. Słowo using
kluczowe używane podczas deklarowania meterListener
powoduje wywoływanie, Dispose
gdy zmienna wykracza poza zakres. Należy pamiętać, że Dispose
obiecuje tylko, że nie zainicjuje nowych wywołań zwrotnych. Ponieważ wywołania zwrotne występują w różnych wątkach, nadal mogą występować wywołania zwrotne po wywołaniu funkcji zwracania Dispose
.
Aby zagwarantować, że określony region kodu w wywołaniu zwrotnym nie jest obecnie wykonywany i nie będzie wykonywany w przyszłości, należy dodać synchronizację wątków. Dispose
domyślnie nie obejmuje synchronizacji, ponieważ:
- Synchronizacja zwiększa obciążenie wydajności w każdym wywołaniu zwrotnym miary.
MeterListener
jest zaprojektowany jako wysoce wydajny interfejs API.