Dela via


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:

Mer information om instrumentation och alternativ för anpassade mått finns i Jämför mått-API:er.

Förutsättningar

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

OpenTelemetry:

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

  1. .NET-mått-API:erna registrerar mått från exempelappen.

  2. OpenTelemetry-biblioteket som körs i appen aggregerar måtten.

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

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

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

  1. Ladda om konfigurationen eller starta om Prometheus-servern.

  2. Bekräfta att OpenTelemetryTest är i UP-tillståndet på sidan Statusmål> i Prometheus-webbportalen. Prometheus status

  3. På sidan Graph i Prometheus-webbportalen anger du hats i textrutan uttryck och väljer hats_sold_Hatshat På diagramfliken visar Prometheus det ökande värdet för den "hattsålda" räknaren som genereras av exempelappen. Prometheus hats sold graph

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

  1. Följ standardanvisningarna för att installera Grafana och ansluta det till en Prometheus-datakälla.

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

    Hats sold Grafana dashboard editor

  3. Klicka på Använd för att spara och visa den nya instrumentpanelen.

    Hats sold Grafana dashboard]

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 bytetyperna , short, int, long, floatoch doubledecimal . 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.