EventCounters v .NET

Tento článek se vztahuje na: ✔️ .NET Core 3.0 SDK a novější verze

EventCounters jsou rozhraní .NET API, která se používají pro odlehčenou, multiplatformní a téměř kolekci metrik výkonu v reálném čase. EventCounters byly přidány jako alternativu pro různé platformy k "čítačům výkonu" .NET Framework na Windows. V tomto článku se dozvíte, co jsou EventCounters, jak je implementovat a jak je využívat.

Modul runtime .NET a několik knihoven .NET publikuje základní diagnostické informace pomocí EventCounters začínajících v .NET Core 3.0. Kromě eventCounters, které poskytuje modul runtime .NET, se můžete rozhodnout implementovat vlastní eventCounters. EventCounters lze použít ke sledování různých metrik. Další informace o nich v známých eventCounters v .NET

EventCounters žijí jako součást programu EventSourcea automaticky se nasdílí do nástrojů naslouchacího procesu pravidelně. Stejně jako všechny ostatní události na platformě EventSource, mohou být využity jak in-proc, tak out-of-proc prostřednictvím EventListener a EventPipe. Tento článek se zaměřuje na možnosti služby EventCounters pro různé platformy a záměrně vylučuje funkci PerfView a ETW (Trasování událostí pro Windows), i když se obě možnosti dají použít se službou EventCounters.

EventCounters in-proc and out-of-proc diagram image

Přehled rozhraní API EventCounter

Existují dvě primární kategorie EventCounters. Některé čítače jsou určené pro hodnoty rychlosti, jako je celkový počet výjimek, celkový počet GC a celkový počet požadavků. Další čítače jsou hodnoty snímku, jako jsou využití haldy, využití procesoru a velikost pracovní sady. V každé z těchto kategorií čítačů existují dva typy čítačů, které se liší podle toho, jak získají jejich hodnotu. Čítače dotazování načítají jejich hodnotu prostřednictvím zpětného volání a čítače bez dotazování mají své hodnoty přímo nastavené na instanci čítače.

Čítače jsou reprezentovány následujícími implementacemi:

Naslouchací proces událostí určuje, jak dlouho jsou intervaly měření. Na konci každého intervalu se hodnota přenáší do naslouchacího procesu pro každý čítač. Implementace čítače určují, jaká rozhraní API a výpočty se používají k vytvoření hodnoty v jednotlivých intervalech.

  • Zaznamenává EventCounter sadu hodnot. Metoda EventCounter.WriteMetric přidá do sady novou hodnotu. V každém intervalu se vypočítá statistický souhrn sady, například min, max a průměr. Nástroj dotnet-counters vždy zobrazí střední hodnotu. Je EventCounter užitečné popsat samostatnou sadu operací. Běžné využití může zahrnovat monitorování průměrné velikosti v bajtech nedávných vstupně-výstupních operací nebo průměrnou peněžní hodnotu sady finančních transakcí.

  • Zaznamenává IncrementingEventCounter průběžný součet pro každý časový interval. Metoda IncrementingEventCounter.Increment se přidá do součtu. Pokud Increment() je například volána třikrát během jednoho intervalu s hodnotami 1, 2a 5pak bude průběžný součet 8 hlášen jako hodnota čítače pro tento interval. Nástroj dotnet-counters zobrazí rychlost jako zaznamenaný celkový součet a čas. Je IncrementingEventCounter užitečné měřit, jak často se akce vyskytuje, například počet zpracovaných požadavků za sekundu.

  • Používá PollingCounter zpětné volání k určení hodnoty, která je hlášena. Při každém časovém intervalu se vyvolá funkce zpětného volání poskytnuté uživatelem a vrácená hodnota se použije jako hodnota čítače. K dotazování na metriku z externího zdroje můžete použít A PollingCounter , například získání aktuálních volných bajtů na disku. Dá se také použít k sestavě vlastních statistik, které je možné vypočítat na vyžádání aplikací. Mezi příklady patří hlášení 95. percentilu nedávných latencí požadavků nebo aktuálního dosažení nebo zmeškaného poměru mezipaměti.

  • Pomocí IncrementingPollingCounter zpětného volání určí hlášenou hodnotu přírůstku. Při každém časovém intervalu se vyvolá zpětné volání a potom rozdíl mezi aktuálním vyvoláním a posledním vyvoláním je hlášená hodnota. Nástroj dotnet-counters vždy zobrazí rozdíl jako rychlost, hlášenou hodnotu / čas. Tento čítač je užitečný, pokud není možné volat rozhraní API pro každý výskyt, ale je možné dotazovat celkový počet výskytů. Můžete například nahlásit počet bajtů zapsaných do souboru za sekundu, a to i bez oznámení při každém zápisu bajtu.

Implementace zdroje událostí

Následující kód implementuje ukázku EventSource vystavenou jako pojmenovaného "Sample.EventCounter.Minimal" zprostředkovatele. Tento zdroj obsahuje reprezentaci EventCounter doby zpracování požadavků. Takový čítač má název (tj. jedinečné ID ve zdroji) a zobrazovaný název, který používá nástroje naslouchacího procesu, jako je například dotnet-counter.

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);
    }
}

Slouží dotnet-counters ps k zobrazení seznamu procesů .NET, které je možné monitorovat:

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

EventSource Předejte název možnosti --counters zahájení monitorování čítače:

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

Následující příklad ukazuje výstup monitorování:

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

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

Stisknutím klávesy q zastavte příkaz monitorování.

Podmíněné čítače

Při implementaci EventSource, obsahující čítače lze podmíněně vytvořit instanci při EventSource.OnEventCommand volání metody s Command hodnotou .EventCommand.Enable Chcete-li bezpečně vytvořit instanci čítače pouze v případě, že je null, použijte operátor přiřazení přiřazení s hodnotou null-coalescing. Kromě toho mohou vlastní metody vyhodnotit metodu IsEnabled , aby určila, jestli je aktuální zdroj událostí povolený nebo ne.

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);
    }
}

Tip

Podmíněné čítače jsou čítače, které jsou podmíněně instantovány, mikro-optimalizace. Modul runtime přijímá tento model pro scénáře, kdy se čítače obvykle nepoužívají, aby se ušetřil zlomek milisekundy.

Ukázkové čítače modulu runtime .NET Core

V modulu runtime .NET Core existuje mnoho skvělých příkladů implementací. Tady je implementace modulu runtime pro čítač, který sleduje velikost pracovní sady aplikace.

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

PollingCounter Hlásí aktuální množství fyzické paměti mapované na proces (pracovní sadu) aplikace, protože zaznamenává metriku v okamžiku v čase. Zpětné volání pro dotazování hodnoty je zadaný výraz lambda, což je jenom volání System.Environment.WorkingSet rozhraní API. DisplayName a DisplayUnits jsou volitelné vlastnosti, které lze nastavit tak, aby pomohly spotřebiteli na straně čítače zobrazit hodnotu jasněji. Například dotnet-counters používá tyto vlastnosti k zobrazení popisnější verze názvů čítačů.

Důležité

Vlastnosti DisplayName nejsou lokalizované.

PollingCounterPro , a IncrementingPollingCounter, nic jiného nemusí být provedeno. Oba dotazují hodnoty sami v intervalu požadovaném příjemcem.

Tady je příklad čítače modulu runtime implementovaného pomocí IncrementingPollingCounter.

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

Rozhraní IncrementingPollingCounter API používá Monitor.LockContentionCount k hlášení přírůstku celkového počtu kolizí uzamčení. Vlastnost DisplayRateTimeScale je volitelná, ale při použití může poskytnout nápovědu k tomu, na jaký časový interval se čítač nejlépe zobrazí. Například počet kolizí zámku se nejlépe zobrazí jako počet za sekundu, takže je DisplayRateTimeScale nastaven na jednu sekundu. Rychlost zobrazení lze upravit pro různé typy čítačů rychlosti.

Poznámka

Není DisplayRateTimeScale používán čítači dotnet-counters a naslouchací procesy událostí se nevyžadují k jeho použití.

Existuje více implementací čítačů, které se mají použít jako odkaz v úložišti modulu runtime .NET .

Souběžnost

Tip

Rozhraní API EventCounters nezaručuje bezpečnost vláken. Když delegáti předají nebo PollingCounterIncrementingPollingCounter instance volají více vláken, je vaší zodpovědností zaručit bezpečnost vláken delegátů.

Pokud chcete například sledovat požadavky, zvažte následující 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);
    }
}

Metodu AddRequest() lze volat z obslužné rutiny požadavku a RequestRateCounter dotazuje hodnotu v intervalu určeném příjemcem čítače. AddRequest() Metoda však může být volána více vlákny najednou, umístit časovou podmínku na _requestCount. Alternativní způsob, jak zvýšit _requestCount vlákno bezpečné, je použít Interlocked.Increment.

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

Chcete-li zabránit přetrženým čtením (v 32bitových architekturách) longpoužití Interlocked.Readpole -field _requestCount .

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

Využití EventCounters

Existují dva primární způsoby využívání EventCounters: in-proc a out-of-proc. Spotřebu EventCounters lze odlišit do tří vrstev různých technologií, které využívají.

  • Přenos událostí v nezpracovaného datovém proudu prostřednictvím etW nebo EventPipe:

    Rozhraní ETW API jsou součástí operačního systému Windows a EventPipe je přístupná jako rozhraní .NET API nebo diagnostický protokol IPC.

  • Dekódování binárního streamu událostí do událostí:

    Knihovna TraceEvent zpracovává formáty streamů ETW i EventPipe.

  • Nástroje příkazového řádku a grafického uživatelského rozhraní:

    Nástroje jako PerfView (ETW nebo EventPipe), dotnet-counters (pouze EventPipe) a dotnet-monitor (pouze EventPipe).

Spotřebovávat out-of-proc

Používání eventCounters mimo proc je běžný přístup. Pomocí dotnet-counters je můžete využívat napříč platformami prostřednictvím EventPipe. Tento dotnet-counters nástroj je globální nástroj rozhraní příkazového řádku pro různé platformy, který lze použít k monitorování hodnot čítačů. Pokud chcete zjistit, jak monitorovat dotnet-counters čítače, přečtěte si téma dotnet-counters nebo projděte si kurz Měření výkonu pomocí EventCounters .

dotnet-trace

Nástroj dotnet-trace lze použít k využívání dat čítačů prostřednictvím eventPipe. Tady je příklad použití dotnet-trace ke shromažďování dat čítačů.

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

Další informace o tom, jak v průběhu času shromažďovat hodnoty čítačů, najdete v dokumentaci k dotnet-trace .

Azure Application Insights

Služba Azure Monitor může využívat služby EventCounters, konkrétně Aplikace Azure Přehledy. Čítače je možné přidat a odebrat a můžete zadat vlastní čítače nebo dobře známé čítače. Další informace najdete v tématu Přizpůsobení čítačů, které se mají shromažďovat.

dotnet-monitor

Nástroj dotnet-monitor je experimentální nástroj, který usnadňuje přístup k diagnostickým informacím v procesu .NET. Tento nástroj slouží jako nadmnožina všech diagnostických nástrojů. Kromě trasování může monitorovat metriky, shromažďovat výpisy paměti a shromažďovat výpisy paměti. Distribuuje se jako nástroj rozhraní příkazového řádku i image Dockeru. Zveřejňuje rozhraní REST API a kolekce diagnostických artefaktů probíhá prostřednictvím volání REST.

Další informace naleznete v tématu Úvod dotnet-monitor, experimentální nástroj.

Využívání in-proc

Hodnoty čítačů můžete využívat prostřednictvím EventListener rozhraní API. Jedná se EventListener o způsob, jak využívat všechny události napsané všemi instancemi EventSource vaší aplikace. Další informace o tom, jak používat EventListener rozhraní API, najdete v tématu EventListener.

Nejprve je EventSource potřeba povolit hodnotu čítače. Přepište metodu EventListener.OnEventSourceCreated pro získání oznámení při EventSource vytvoření a pokud je to správná EventSource hodnota u služby EventCounters, můžete ji volat EventListener.EnableEvents . Tady je příklad přepsání:

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"
    });
}

Ukázka kódu

Tady je ukázková EventListener třída, která vytiskne všechny názvy a hodnoty čítačů z modulu runtime EventSource.NET pro publikování interních čítačů (System.Runtime) každých sekund.

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);
    }
}

Jak je znázorněno výše, musíte se ujistit, že "EventCounterIntervalSec" je argument nastaven v argumentu filterPayload při volání EnableEvents. Jinak čítače nebudou moct vyprázdnit hodnoty, protože neví, v jakém intervalu by se měly vyprázdnit.

Viz také