Delen via


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:

Zie Metrische API's vergelijken voor meer informatie over aangepaste metrische instrumentatie en opties.

Vereisten

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

OpenTelemetry:

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

  1. De metrische .NET-API's registreren metingen uit de voorbeeld-app.

  2. De OpenTelemetry-bibliotheek die in de app wordt uitgevoerd, voegt de metingen samen.

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

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

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

  1. Laad de configuratie opnieuw of start de Prometheus-server opnieuw op.

  2. Controleer of OpenTelemetryTest de UP-status heeft op de pagina Statusdoelen> van de Prometheus-webportal. Prometheus status

  3. Voer hats op de grafiekpagina van de Prometheus-webportal het tekstvak voor de expressie in en selecteer hats_sold_Hatshat In het grafiektabblad geeft Prometheus de toenemende waarde weer van het teller 'hats-sold' dat door de voorbeeld-app wordt verzonden. Prometheus hats sold graph

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

  1. Volg de standaardinstructies om Grafana te installeren en deze te verbinden met een Prometheus-gegevensbron.

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

    Hats sold Grafana dashboard editor

  3. Klik op Toepassen om het nieuwe dashboard op te slaan en weer te geven.

    Hats sold Grafana dashboard]

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, , floaten decimal doubletypen. 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.