EventCounters a .NET-ben

Ez a cikk a következőre vonatkozik: ✔️ .NET Core 3.0 SDK és újabb verziók

Az EventCounters .NET API-k egyszerű, platformfüggetlen és közel valós idejű teljesítménymetrikagyűjtéshez használatosak. Az EventCounters platformfüggetlen alternatívaként lett hozzáadva a Windows .NET-keretrendszer teljesítményszámlálóihoz. Ebben a cikkben megtudhatja, hogy mik az EventCountersek, hogyan implementálhatja őket, és hogyan használhatja fel őket.

A .NET-futtatókörnyezet és néhány .NET-kódtár a .NET Core 3.0-tól kezdődőEn az EventCounters használatával teszi közzé az alapvető diagnosztikai információkat. A .NET-futtatókörnyezet által biztosított EventCounters mellett saját EventCounters-eket is implementálhat. Az EventCounters különböző metrikák nyomon követésére használható. További információ ezekről a .NET-ben ismert EventCountersben

Az EventCounters egy EventSourcerészeként él, és rendszeresen automatikusan leküldi a figyelőeszközökre. A többi eseményhez EventSourcehasonlóan az in-proc és a out-of-proc is felhasználható az EventPipe-en keresztül EventListener. Ez a cikk az EventCounters platformfüggetlen képességeire összpontosít, és szándékosan kizárja a PerfView-t és az ETW-t (Windows-eseménykövetés) – bár mindkettő használható az EventCounters használatával.

EventCounters in-proc és out-of-proc diagram képe

Az EventCounter API áttekintése

Az EventCounters két elsődleges kategóriát tartalmaz. Egyes számlálók a "ráta" értékekhez tartoznak, például a kivételek teljes száma, a GCs-k teljes száma és a kérelmek teljes száma. Más számlálók a "pillanatkép" értékek, például a halomhasználat, a processzorhasználat és a munkakészlet mérete. A számlálók ezen kategóriáiban két számlálótípus létezik, amelyek az értékük lekérésétől függően változnak. A lekérdezésszámlálók visszahívással kérik le az értéküket, a nem lekérdezési számlálók pedig közvetlenül a számlálópéldányon vannak beállítva.

A számlálókat a következő implementációk képviselik:

Az eseményfigyelő megadja, hogy mennyi a mérési időköz. Az egyes intervallumok végén egy érték lesz továbbítva a figyelőnek az egyes számlálókhoz. A számláló implementációi határozzák meg, hogy milyen API-kat és számításokat használnak az egyes intervallumok értékének előállításához.

  • A EventCounter rekordok egy értékkészletet rögzítenek. A EventCounter.WriteMetric metódus új értéket ad hozzá a készlethez. Az egyes intervallumok esetében a rendszer kiszámítja a készlet statisztikai összegzését, például a minimális, a maximális és a középértékeket. A dotnet-counters eszköz mindig megjeleníti a középértéket. Ez EventCounter hasznos a különálló műveletek leírásához. A gyakori használat magában foglalhatja a legutóbbi IO-műveletek bájtban kifejezett átlagos méretét vagy egy pénzügyi tranzakció átlagos pénzügyi értékét.

  • Az IncrementingEventCounter egyes időintervallumok futási összegét rögzíti. A IncrementingEventCounter.Increment metódus hozzáadja az összeghez. Ha például Increment() egy intervallumban háromszor hívjuk meg az értékeket 1, 2és 5a futó összeg 8 lesz az intervallum számlálóértékeként jelentve. A dotnet-counters eszköz megjeleníti a ráta a rögzített összeg / idő. Ez IncrementingEventCounter hasznos annak méréséhez, hogy milyen gyakran történik egy művelet, például a másodpercenként feldolgozott kérelmek száma.

  • A PollingCounter jelentés értéke visszahívással határozható meg. Minden időintervallum esetén a rendszer meghívja a felhasználó által megadott visszahívási függvényt, és a visszatérési értéket használja számlálóértékként. A PollingCounter metrika külső forrásból való lekérdezésére használható, például az aktuális szabad bájtok lekérésére egy lemezen. Az alkalmazás igény szerint kiszámítható egyéni statisztikák jelentésére is használható. Ilyenek például a legutóbbi kérések késésének 95. percentilisének jelentése, vagy a gyorsítótár aktuális találati vagy kihagyási aránya.

  • A IncrementingPollingCounter visszahívással határozza meg a jelentett növekmény értékét. Minden időintervallum esetén a rendszer meghívja a visszahívást, majd az aktuális hívás és az utolsó hívás közötti különbség a jelentett érték. A dotnet-counters eszköz mindig megjeleníti a különbséget, mint egy ráta, a jelentett érték / idő. Ez a számláló akkor hasznos, ha nem lehet api-t meghívni minden előforduláshoz, de lekérdezhető az előfordulások teljes száma. Jelentheti például a fájlba írt bájtok másodpercenkénti számát, még értesítés nélkül is minden bájt megírásakor.

EventSource implementálása

Az alábbi kód egy nevesített "Sample.EventCounter.Minimal" szolgáltatóként közzétett mintát EventSource valósít meg. Ez a forrás egy EventCounter kérésfeldolgozási időt jelöl. Az ilyen számlálók neve (azaz a forrás egyedi azonosítója) és egy megjelenítendő név, amelyet a figyelőeszközök, például a dotnet-counters egyaránt használnak.

using System.Diagnostics.Tracing;

[EventSource(Name = "Sample.EventCounter.Minimal")]
public sealed class MinimalEventCounterSource : EventSource
{
    public static readonly MinimalEventCounterSource Log = new MinimalEventCounterSource();

    private EventCounter _requestCounter;

    private MinimalEventCounterSource() =>
        _requestCounter = new EventCounter("request-time", this)
        {
            DisplayName = "Request Processing Time",
            DisplayUnits = "ms"
        };

    public void Request(string url, long elapsedMilliseconds)
    {
        WriteEvent(1, url, elapsedMilliseconds);
        _requestCounter?.WriteMetric(elapsedMilliseconds);
    }

    protected override void Dispose(bool disposing)
    {
        _requestCounter?.Dispose();
        _requestCounter = null;

        base.Dispose(disposing);
    }
}

A figyelhető .NET-folyamatok listájának megjelenítésére használható dotnet-counters ps :

dotnet-counters ps
   1398652 dotnet     C:\Program Files\dotnet\dotnet.exe
   1399072 dotnet     C:\Program Files\dotnet\dotnet.exe
   1399112 dotnet     C:\Program Files\dotnet\dotnet.exe
   1401880 dotnet     C:\Program Files\dotnet\dotnet.exe
   1400180 sample-counters C:\sample-counters\bin\Debug\netcoreapp3.1\sample-counters.exe

Adja meg a EventSource nevet a --counters számláló figyelésének megkezdéséhez:

dotnet-counters monitor --process-id 1400180 --counters Sample.EventCounter.Minimal

Az alábbi példa a monitor kimenetét mutatja be:

Press p to pause, r to resume, q to quit.
    Status: Running

[Samples-EventCounterDemos-Minimal]
    Request Processing Time (ms)                            0.445

A monitorozási parancs leállításához nyomja le a q billentyűt.

Feltételes számlálók

A EventSourceimplementáláskor a számlálók feltételesen példányosíthatók, ha a EventSource.OnEventCommand metódust a rendszer a következő értékkel EventCommand.Enablehívja megCommand: . Ha csak abban az nullesetben szeretne biztonságosan példányosítani egy számlálópéldányt, használja a null-szenes hozzárendelés-operátort. Emellett az egyéni metódusok kiértékelhetik a IsEnabled metódust annak megállapításához, hogy engedélyezve van-e az aktuális eseményforrás.

using System.Diagnostics.Tracing;

[EventSource(Name = "Sample.EventCounter.Conditional")]
public sealed class ConditionalEventCounterSource : EventSource
{
    public static readonly ConditionalEventCounterSource Log = new ConditionalEventCounterSource();

    private EventCounter _requestCounter;

    private ConditionalEventCounterSource() { }

    protected override void OnEventCommand(EventCommandEventArgs args)
    {
        if (args.Command == EventCommand.Enable)
        {
            _requestCounter ??= new EventCounter("request-time", this)
            {
                DisplayName = "Request Processing Time",
                DisplayUnits = "ms"
            };
        }
    }

    public void Request(string url, float elapsedMilliseconds)
    {
        if (IsEnabled())
        {
            _requestCounter?.WriteMetric(elapsedMilliseconds);
        }
    }

    protected override void Dispose(bool disposing)
    {
        _requestCounter?.Dispose();
        _requestCounter = null;

        base.Dispose(disposing);
    }
}

Tipp.

A feltételes számlálók feltételesen példányosított számlálók, mikrooptimalizálással. A futtatókörnyezet ezt a mintát alkalmazza olyan helyzetekben, ahol a számlálókat általában nem használják, az ezredmásodperc töredékének mentéséhez.

.NET Core-futtatókörnyezeti példaszámlálók

A .NET Core-futtatókörnyezetben számos nagyszerű példa implementáció található. Itt található a számláló futtatókörnyezeti implementációja, amely nyomon követi az alkalmazás munkakészletének méretét.

var workingSetCounter = new PollingCounter(
    "working-set",
    this,
    () => (double)(Environment.WorkingSet / 1_000_000))
{
    DisplayName = "Working Set",
    DisplayUnits = "MB"
};

A PollingCounter jelentés az alkalmazás folyamatához (munkakészletéhez) hozzárendelt fizikai memória aktuális mennyiségét jelenti, mivel egy metrikát rögzít egy adott pillanatban. Az érték lekérdezésének visszahívása a megadott lambda kifejezés, amely csak az API hívása System.Environment.WorkingSet . DisplayName és DisplayUnits választható tulajdonságok, amelyek beállíthatók úgy, hogy segítsenek a számláló fogyasztói oldalán az érték egyértelműbb megjelenítésében. A dotnet-counters például ezeket a tulajdonságokat használja a számlálónevek megjeleníthetőbb verziójának megjelenítéséhez.

Fontos

A DisplayName tulajdonságok nincsenek honosítva.

PollingCounterA , és a IncrementingPollingCounter, semmi mást nem kell tenni. Mindketten maguk kérdezik le az értékeket a fogyasztó által kért időközönként.

Íme egy példa egy futásidejű számlálóra, amely a következő használatával implementálva van IncrementingPollingCounter: .

var monitorContentionCounter = new IncrementingPollingCounter(
    "monitor-lock-contention-count",
    this,
    () => Monitor.LockContentionCount
)
{
    DisplayName = "Monitor Lock Contention Count",
    DisplayRateTimeScale = TimeSpan.FromSeconds(1)
};

Az IncrementingPollingCounter API használatával Monitor.LockContentionCount jelenti a teljes zárolási versengés számának növekményét. A DisplayRateTimeScale tulajdonság nem kötelező, de használat esetén jelzi, hogy a számláló melyik időintervallumban jelenik meg a legjobban. A zárolási versengés száma például másodpercenkénti számként jelenik meg a legjobban, így DisplayRateTimeScale az értéke egy másodpercre van állítva. A megjelenítési sebesség különböző típusú sebességszámlálókhoz módosítható.

Feljegyzés

A DisplayRateTimeScaledotnet-counterk nem használják, és az eseményfigyelőknek nem kell használniuk.

A .NET futtatókörnyezeti adattárban több számláló implementáció is használható referenciaként.

Egyidejűség

Tipp.

Az EventCounters API nem garantálja a szál biztonságát. Ha a meghatalmazottaknak PollingCounter átadott vagy IncrementingPollingCounter példányokat több szál hívja meg, az Ön felelőssége, hogy garantálja a meghatalmazottak szálbiztonságát.

A kérések nyomon követéséhez például vegye figyelembe az alábbiakat EventSource .

using System;
using System.Diagnostics.Tracing;

public class RequestEventSource : EventSource
{
    public static readonly RequestEventSource Log = new RequestEventSource();

    private IncrementingPollingCounter _requestRateCounter;
    private long _requestCount = 0;

    private RequestEventSource() =>
        _requestRateCounter = new IncrementingPollingCounter("request-rate", this, () => _requestCount)
        {
            DisplayName = "Request Rate",
            DisplayRateTimeScale = TimeSpan.FromSeconds(1)
        };

    public void AddRequest() => ++ _requestCount;

    protected override void Dispose(bool disposing)
    {
        _requestRateCounter?.Dispose();
        _requestRateCounter = null;

        base.Dispose(disposing);
    }
}

A AddRequest() metódus meghívható egy kérelemkezelőtől, és a RequestRateCounter számláló fogyasztója által megadott időközönként kérdezi le az értéket. A AddRequest() metódust azonban egyszerre több szál is meghívhatja, ami versenyhelyzetet hoz létre._requestCount A szálbiztos alternatív módszer a _requestCount használat növelésére Interlocked.Increment.

public void AddRequest() => Interlocked.Increment(ref _requestCount);

A -field _requestCount használat Interlocked.Readmegszakadt olvasásának megakadályozása (32 bites architektúrákonlong).

_requestRateCounter = new IncrementingPollingCounter("request-rate", this, () => Interlocked.Read(ref _requestCount))
{
    DisplayName = "Request Rate",
    DisplayRateTimeScale = TimeSpan.FromSeconds(1)
};

EventCounters használata

Az EventCounters kétféleképpen fogyasztható: in-proc és out-of-proc. Az EventCounters használata a különböző fogyasztó technológiák három rétegében különböztethető meg.

  • Események átvitele nyers streamben ETW-en vagy EventPipe-en keresztül:

    Az ETW API-k a Windows operációs rendszert használják, az EventPipe pedig .NET API-ként vagy diagnosztikai IPC protokollként érhető el.

  • A bináris eseményfolyam dekódolása eseményekké:

    A TraceEvent kódtár az ETW és az EventPipe streamformátumokat is kezeli.

  • Parancssori és grafikus felhasználói felület eszközei:

    Olyan eszközök, mint a PerfView (ETW vagy EventPipe), a dotnet-counters (csak EventPipe) és a dotnet-monitor (csak EventPipe).

Használaton kívüli használat

Az EventCounters igényen kívüli felhasználása gyakori módszer. A dotnet-counters használatával platformfüggetlen módon használhatja fel őket egy EventPipe-en keresztül. Az dotnet-counters eszköz egy platformfüggetlen dotnet CLI globális eszköz, amely a számlálóértékek figyelésére használható. A számlálók monitorozásának módjáról dotnet-counters a dotnet-counters című témakörben olvashat, vagy az EventCounters-oktatóanyag segítségével végigjárhatja a Mérték teljesítményét.

dotnet-trace

Az dotnet-trace eszköz használható a számlálóadatok EventPipe-on keresztüli felhasználására. Íme egy példa dotnet-trace számlálóadatok gyűjtésére.

dotnet-trace collect --process-id <pid> Sample.EventCounter.Minimal:0:0:EventCounterIntervalSec=1

A számlálóértékek időbeli gyűjtéséről a dotnet-trace dokumentációjában talál további információt.

Azure Application Insights

Az EventCounterst az Azure Monitor használhatja, különösen Azure-alkalmazás Elemzések. A számlálók hozzáadhatók és eltávolíthatók, és szabadon megadhat egyéni számlálókat vagy jól ismert számlálókat. További információ: Gyűjtendő számlálók testreszabása.

dotnet-monitor

Az dotnet-monitor eszköz megkönnyíti a diagnosztikát egy .NET-folyamatból távoli és automatizált módon. A nyomkövetések mellett figyelheti a metrikákat, memóriaképeket és GC-memóriaképeket is gyűjthet. CLI-eszközként és docker-rendszerképként is el van osztva. EGY REST API-t tesz elérhetővé, és a diagnosztikai összetevők gyűjteménye REST-hívásokon keresztül történik.

További információ: dotnet-monitor.

Használaton belüli használat

A számlálóértékeket az EventListener API-val használhatja fel. Az an EventListener az alkalmazás összes példánya által írt események be- és lekéréses EventSource felhasználásának módja. Az API használatával kapcsolatos további információkért EventListener lásd EventListener: .

Először engedélyezni kell a EventSource számláló értékét előállító értéket. Felülbírálhatja a EventListener.OnEventSourceCreated metódust, hogy értesítést kapjon egy létrehozáskor EventSource , és ha ez a helyes EventSource az EventCountersben, akkor meghívhatja EventListener.EnableEvents azt. Íme egy példa felülbírálásra:

protected override void OnEventSourceCreated(EventSource source)
{
    if (!source.Name.Equals("System.Runtime"))
    {
        return;
    }

    EnableEvents(source, EventLevel.Verbose, EventKeywords.All, new Dictionary<string, string>()
    {
        ["EventCounterIntervalSec"] = "1"
    });
}

Mintakód

Íme egy mintaosztály EventListener , amely a .NET-futtatókörnyezet összes számlálónevét és értékét kinyomtatja EventSourcea belső számlálók (System.Runtime) másodpercenkénti közzétételéhez.

using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;

public class SimpleEventListener : EventListener
{
    public SimpleEventListener()
    {
    }

    protected override void OnEventSourceCreated(EventSource source)
    {
        if (!source.Name.Equals("System.Runtime"))
        {
            return;
        }

        EnableEvents(source, EventLevel.Verbose, EventKeywords.All, new Dictionary<string, string>()
        {
            ["EventCounterIntervalSec"] = "1"
        });
    }

    protected override void OnEventWritten(EventWrittenEventArgs eventData)
    {
        if (!eventData.EventName.Equals("EventCounters"))
        {
            return;
        }

        for (int i = 0; i < eventData.Payload.Count; ++ i)
        {
            if (eventData.Payload[i] is IDictionary<string, object> eventPayload)
            {
                var (counterName, counterValue) = GetRelevantMetric(eventPayload);
                Console.WriteLine($"{counterName} : {counterValue}");
            }
        }
    }

    private static (string counterName, string counterValue) GetRelevantMetric(
        IDictionary<string, object> eventPayload)
    {
        var counterName = "";
        var counterValue = "";

        if (eventPayload.TryGetValue("DisplayName", out object displayValue))
        {
            counterName = displayValue.ToString();
        }
        if (eventPayload.TryGetValue("Mean", out object value) ||
            eventPayload.TryGetValue("Increment", out value))
        {
            counterValue = value.ToString();
        }

        return (counterName, counterValue);
    }
}

Ahogy fentebb látható, meg kell győződnie arról, hogy az "EventCounterIntervalSec" argumentum be van állítva az argumentumban híváskor filterPayloadEnableEvents. Ellenkező esetben a számlálók nem tudják kiüríteni az értékeket, mivel nem tudja, hogy milyen időközönként kell kiüríteni.

Lásd még