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:

Aby uzyskać więcej informacji na temat instrumentacji i opcji metryk niestandardowych, zobacz Porównanie interfejsów API metryk.

Wymagania wstępne

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

OpenTelemetry:

  • 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:

  1. Interfejsy API metryk platformy .NET rejestrują pomiary z przykładowej aplikacji.

  2. Biblioteka OpenTelemetry uruchomiona w aplikacji agreguje miary.

  3. 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.

  4. 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
  5. 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.

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

  1. Załaduj ponownie konfigurację lub uruchom ponownie serwer Prometheus.

  2. Upewnij się, że plik OpenTelemetryTest jest w stanie UP na stronie Cele stanu>w portalu internetowym Prometheus. Prometheus status

  3. Na stronie Graf portalu internetowego Prometheus wprowadź hats w polu tekstowym wyrażenie i wybierz pozycję hats_sold_Hatshat Na karcie grafu Prometheus pokazuje rosnącą wartość licznika "hats-sold" emitowanego przez przykładową aplikację. Prometheus hats sold graph

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

  1. Postępuj zgodnie ze standardowymi instrukcjami , aby zainstalować aplikację Grafana i połączyć ją ze źródłem danych Prometheus.

  2. 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:

    Hats sold Grafana dashboard editor

  3. Kliknij przycisk Zastosuj , aby zapisać i wyświetlić nowy pulpit nawigacyjny.

    Hats sold Grafana dashboard]

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 bytetypów , , longfloatdoubleshortint, 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.