Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Ten artykuł dotyczy: ✔️ .NET 6.0 i nowszych ✔️ .NET Framework 4.6.1 i nowszych
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 tutorialu przedstawiono kilka przykładów zbierania metryk.
- Wypełnianie metryk w narzędziu Grafana za pomocą OpenTelemetry i Prometheus.
- Wyświetlanie metryk w czasie rzeczywistym za pomocą polecenia
dotnet-counters - Tworzenie niestandardowego narzędzia do tworzenia kolekcji przy użyciu bazowego interfejsu API platformy .NET MeterListener.
Aby uzyskać więcej informacji na temat niestandardowej instrumentacji metryk i dostępnych opcji, sprawdź Porównanie interfejsów API metryk.
Wymagania wstępne
- .NET 6.0 SDK lub nowszy
Tworzenie przykładowej aplikacji
Aby można było zbierać metryki, należy wygenerować pomiary. Ten samouczek tworzy aplikację z podstawową instrumentacją metryczną. Ś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 o losowych godzinach.
Wyświetlanie metryk za pomocą dotnet-counters
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 badania doraźne lub weryfikację działania instrumentacji metryk. Działa zarówno z interfejsami API opartymi na System.Diagnostics.Metrics jak i EventCounters.
Jeśli narzędzie dotnet-counters nie jest zainstalowane, uruchom następujące polecenie:
dotnet tool update -g dotnet-counters
Jeśli aplikacja korzysta z wersji platformy .NET starszej niż .NET 9, wyjściowy interfejs użytkownika liczników dotnet-counter będzie wyglądać nieco inaczej niż poniżej; Aby uzyskać szczegółowe informacje, zobacz dotnet-counters .
Gdy przykładowa aplikacja jest uruchomiona, uruchom dotnet-counters. Poniższe polecenie przedstawia przykład dotnet-counters monitorowania wszystkich metryk z miernika HatCo.HatStore . Nazwa miernika jest rozróżniana ze względu na wielkość liter. Nasza przykładowa aplikacja to metric-instr.exe, zastąp tę nazwę nazwą swojej 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ć z innym zestawem metryk, aby zobaczyć niektóre wbudowane mechanizmy instrumentowania środowiska uruchomieniowego platformy .NET.
dotnet-counters monitor -n metric-instr
Wyświetlane są dane wyjściowe podobne do następujących:
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
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 Grafanie z użyciem OpenTelemetry i Prometheus.
Przegląd
- 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 .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 open-source Prometheus i Grafana. Przepływ danych metryk:
API metryk platformy .NET rejestruje pomiary z aplikacji przykładowej.
Biblioteka OpenTelemetry uruchomiona w aplikacji agreguje miary.
Biblioteka eksportera Prometheus udostępnia zagregowane dane za pośrednictwem punktu końcowego metryk HTTP. W terminologii OpenTelemetry "Eksporter" to 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 określa odczytywanie i przechowywanie danych jako pobieranie z 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));
}
}
}
W poprzednim kodzie:
-
AddMeter("HatCo.HatStore")konfiguruje OpenTelemetry do przesyłania wszystkich metryk zebranych przez miernik zdefiniowany w aplikacji. -
AddPrometheusHttpListenerKonfiguruje bibliotekę OpenTelemetry w taki sposób, aby:- Uwidaczniaj punkt końcowy metryk rozwiązania Prometheus na porcie
9184 - Użyj 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
Skonfiguruj i ustaw Prometeusza
Wykonaj pierwsze kroki rozwiązania Prometheus , aby skonfigurować serwer Prometheus i upewnij się, że działa.
Zmodyfikuj plik konfiguracyjny prometheus.yml tak, aby Prometheus pobierał metryki z punktu końcowego udostępnianego przez przykładową aplikację. 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 OpenTelemetryTest jest w stanie UP na stronie Status>Cele portalu internetowego Prometheus.
Na stronie Graf portalu internetowego Prometheus wprowadź
hatsw polu tekstowym wyrażenia i wybierz pozycjęhats_sold_Hats
. Na karcie Graf, 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 zbiera danych z przykładowej aplikacji przez długi czas, może być konieczne poczekanie na zebranie danych.
Wyświetlanie metryk na pulpicie 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 Tytuł i rate(hats_sold[5m]) w polu wyrażenia PromQL.
Kliknij przycisk Zastosuj , aby zapisać i wyświetlić nowy pulpit nawigacyjny.
]
Tworzenie niestandardowego narzędzia do kolekcjonowania przy użyciu .NET MeterListener API
Interfejs API platformy .NET MeterListener umożliwia tworzenie niestandardowej logiki przetwarzania w procesie, aby obserwować pomiary rejestrowane przez System.Diagnostics.Metrics.Meter. Aby uzyskać wskazówki dotyczące tworzenia logiki niestandardowej zgodnej ze starszą instrumentacją EventCounters, zobacz EventCounters.
Zmodyfikuj kod Program.cs tak, aby używał 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 pokazują wynik działania aplikacji z niestandardowym wywołaniem zwrotnym dla każdego pomiaru.
> 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
...
Przykładowe wyjaśnienie kodu
Fragmenty kodu w tej sekcji pochodzą z poprzedniego przykładu.
W poniższym wyróżnionym kodzie tworzone jest wystąpienie MeterListener, aby odbierać pomiary. Słowo kluczowe using powoduje wywołanie Dispose , gdy meterListener opuszcza 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ę, licznik lub każdą 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:
- Zwykle się tego nie robi.
- W dowolnym momencie można wywołać
EnableMeasurementEvents()za pomocą referencji.
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ć za pomocą byte, short, int, long, float, double i decimal typów. Zalecamy rejestrowanie wywołania zwrotnego dla każdego typu danych, chyba że masz wiedzę, że w danym scenariuszu nie wszystkie typy danych są potrzebne. Wykonywanie powtarzających się wywołań do SetMeasurementEventCallback z różnymi argumentami ogólnymi może wydawać się trochę nietypowe. Interfejs API został zaprojektowany w ten sposób, aby umożliwić MeterListener odbieranie pomiarów z niskim obciążeniem wydajnościowym, które zwykle wynosi tylko kilka nanosekund.
Gdy wywoływana jest funkcja MeterListener.EnableMeasurementEvents, obiekt state 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 słuchacze 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.
Alternatywnie, utwórz Dictionary mapujący instrument na obiekt magazynowy i wyszukuj go przy każdym pomiarze. Korzystanie z Dictionary jest znacznie wolniejsze niż uzyskiwanie dostępu z state.
meterListener.Start();
Powyższy kod uruchamia MeterListener, który umożliwia wywołania zwrotne. Delegat InstrumentPublished jest przywoływany dla każdego istniejącego instrumentu w procesie. Nowo utworzone obiekty instrumentów również powodują wywołanie InstrumentPublished.
using MeterListener meterListener = new MeterListener();
Gdy aplikacja zakończy nasłuchiwanie, usunięcie obiektu nasłuchującego zatrzymuje przepływ wywołań zwrotnych i zwalnia wszelkie wewnętrzne referencje do obiektu nasłuchującego. Słowo kluczowe using, używane podczas deklarowania meterListener, powoduje, że Dispose jest wywoływane, 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, mogą one nadal trwać po tym, jak wywołanie Dispose zostanie zakończone.
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 dodaje obciążenie wydajności przy każdym wywołaniu zwrotnym dotyczącym pomiaru.
-
MeterListenerjest zaprojektowany jako interfejs API z dużym naciskiem na wydajność.