Dela via


Samla in mätvärden

Den här artikeln gäller för: ✔️ .NET 6.0 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 handledningen visar flera exempel på insamling av metrik:

Mer information om instrumentering och alternativ för anpassade metriker finns i Jämför metrisk API:er.

Förutsättningar

Skapa en exempelapp

Innan mätvärden kan samlas in måste mätningarna genereras. Den här handledningen skapar en app som har grundläggande meterinstrumentering. .NET runtime har också olika inbyggda mätvärden. 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 att sälja hattar vid slumpmässiga intervall och ögonblick.

Visa mätvärden 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

Om din app kör en version av .NET som är äldre än .NET 9 ser utdatagränssnittet för dotnet-räknare något annorlunda ut än nedan. se dotnet-counters för mer information.

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 liknar följande 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 liknar följande visas:

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

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 genom att använda .NET-mått-API.
  • Stöds av Azure Monitor och många APM-leverantörer.

Den här handledningen visar en av de integrationer som är tillgängliga för OpenTelemetry-mått som använder OSS-projekten Prometheus och Grafana. 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 de bibliotek som överför telemetri till leverantörsspecifika backend-system.

  4. En Prometheus-server:

    • Kontrollerar metrikslutpunkten
    • Läser den 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:

    • Frågar data som lagras i Prometheus och visar det på ett webbaserat övervakningsgränssnitt.
    • 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

Anmärkning

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 metrikslutpunkt 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

Ställ in och konfigurera Prometheus

Följ de första stegen i Prometheus för att konfigurera en Prometheus-server och bekräfta att den fungerar.

Ändra prometheus.yml konfigurationsfilen 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 uttrycksrutan och väljer hats_sold_Hatshatt. På diagramfliken visar Prometheus det ökande värdet för den "sålda-hattar"-räknaren som skickas ut av exempelappen. Graf över sålda Prometheus-hattar

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 Titel och rate(hats_sold[5m]) i fältet för PromQL-uttryck:

    Hats sålde Grafana-instrumentpanelsredigeraren

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

    Hattsåld Grafana-instrumentpanel

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

Exempelkodförklaring

Kodfragmenten i det här avsnittet kommer från föregående exempel.

I följande markerade kod skapas en instans av en MeterListener för att ta emot mätvärden. Nyckelordet using orsakar att Dispose anropas när meterListener går ur sitt omfång.

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 han/hon ska gå med i det. Ombudet kan till exempel kontrollera namnet, mätaren eller någon annan publik 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 Counter<int> en 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();

Den föregående koden startar MeterListener som möjliggör återanrop. Ombudet InstrumentPublished anropas för varje befintligt instrument i processen. När instrumentobjekt skapas utlöses också att InstrumentPublished 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 endast lovar att det inte initierar nya återanrop. Eftersom återuppringningar sker i olika trådar kan det fortfarande finnas återuppringningar som pågår efter att anropet till Dispose returnerar.

För att garantera att en viss kodregion i återanropet inte körs nu eller 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.