EventCounters v .NET

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

Poznámka:

Pro vývoj nových projektů .NET Microsoft doporučuje místo toho používat novější rozhraní API System.Diagnostics.Metrics . Rozhraní API System.Diagnostics.Metrics nabízejí zvýšenou funkčnost, standardizaci a integraci s širším ekosystémem nástrojů. Další informace najdete v porovnání rozhraní API metrik.

EventCounters jsou rozhraní .NET API používaná pro odlehčenou, multiplatformní a téměř kolekci metrik výkonu v reálném čase. EventCounters byly přidány jako multiplatformní alternativu k "čítačům výkonu" rozhraní .NET Framework ve 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 publikují základní diagnostické informace pomocí eventCounterů počínaje v .NET Core 3.0. Kromě eventCounterů, 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 ve známých funkcích EventCounters v .NET

EventCounters žijí jako součást a EventSourceautomaticky se odsílají do nástrojů naslouchacího procesu pravidelně. Stejně jako všechny ostatní události na objektu EventSourcemohou být využity jak in-proc, tak out-of-proc prostřednictvím EventListener a EventPipe. Tento článek se zaměřuje na multiplatformní funkce EventCounters a záměrně vylučuje Nástroj PerfView a Trasování událostí (Trasování událostí pro Windows), i když je možné je použít s EventCounters.

Obrázek diagramu EventCounters in-proc a out-of-proc

Přehled rozhraní EVENTCounter API

Existují dvě primární kategorie EventCounters. Některé čítače jsou určené pro hodnoty "rate", jako je celkový počet výjimek, celkový počet GCS a celkový počet požadavků. Další čítače jsou hodnoty snímků, 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ískávají jejich hodnotu. Čítače dotazování načítají svou 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 každém intervalu.

  • Zaznamenává EventCounter sadu hodnot. Metoda EventCounter.WriteMetric přidá do sady novou hodnotu. Při každém intervalu se vypočítá statistický souhrn sady, jako je minimum, maximum 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čte celkový součet. Pokud Increment() se například volá třikrát během jednoho intervalu s hodnotami 1, 2a 5pak se průběžný součet 8 bude hlásit 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é změřit, jak často se akce vyskytuje, například počet zpracovaných požadavků za sekundu.

  • Zpětné PollingCounter volání používá 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 návratová hodnota se použije jako hodnota čítače. Dá PollingCounter se použít k dotazování metriky z externího zdroje, například získání aktuálních volných bajtů na disku. Dá se také použít k hlášení vlastních statistik, které může aplikace vypočítat na vyžádání. Mezi příklady patří hlášení 95. percentilu nedávných latencí požadavků nebo aktuálního dosažení nebo neúspěšného poměru mezipamětí.

  • 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 a čas. Tento čítač je užitečný, pokud není možné volat rozhraní API pro každý výskyt, ale je možné zadat dotaz na celkový počet výskytů. Můžete například hlá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. jeho jedinečné ID ve zdroji) a zobrazovaný název, který používají nástroje naslouchacího procesu, jako jsou čítače dotnet.

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é lze 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 --counters možnosti pro 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 zavolá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í při sjednocení s hodnotou null. 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ě instantní, mikro-optimalizace. Modul runtime používá 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 ukázkových 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 namapované na proces (pracovní sadu) aplikace, protože zachytává metriku v okamžiku v čase. Zpětné volání pro dotazování hodnoty je zadaný výraz lambda, což je jen volání System.Environment.WorkingSet rozhraní API. DisplayName a DisplayUnits jsou volitelné vlastnosti, které lze nastavit tak, aby pomohly straně příjemce čí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 , nic jiného IncrementingPollingCounternemusí 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 nepovinná, ale při použití může poskytnout nápovědu k časovému intervalu, ve kterém se čítač nejlépe zobrazí. Například počet kolizí zámků se nejlépe zobrazí jako počet za sekundu, takže DisplayRateTimeScale je nastavený na jednu sekundu. Rychlost zobrazení lze upravit pro různé typy čítačů sazeb.

Poznámka:

Není DisplayRateTimeScale používán čítači dotnet a naslouchací procesy událostí ji nevyžadují.

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

Souběžnost

Tip

Rozhraní EventCounters API nezaručuje bezpečnost vláken. Když delegáti předají nebo IncrementingPollingCounter jsou voláni PollingCounter více vlákny, je vaší zodpovědností zaručit bezpečnost vláken delegátů.

Představte si například následující EventSource informace o požadavcích.

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. Nicméně, AddRequest() metoda může být volána více vláken najednou, což dává časová podmínka na _requestCount. Alternativou pro zvýšení _requestCount počtu vláken je použití 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.Read-field _requestCount .

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

Využívání objektů EventCounters

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

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

    Rozhraní API pro Windows se dodávají s operačním systémem Windows a EventPipe je přístupná jako rozhraní .NET API nebo diagnostický protokol IPC.

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

    Knihovna TraceEvent zpracovává formáty streamu ETW i EventPipe.

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

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

Využití mimo proc

Využívání eventCounters mimo proc je běžný přístup. Pomocí čítačů dotnet-counter 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 dotnet pro různé platformy, který lze použít k monitorování hodnot čítačů. Pokud chcete zjistit, jak dotnet-counters monitorovat čítače, přečtěte si téma dotnet-counters nebo si projděte kurz Měření výkonu pomocí EventCounters .

Azure Application Insights

Azure Monitor může využívat eventCounters, konkrétně Aplikace Azure lication Insights. Čítače je možné přidávat a odebírat a můžete zadat vlastní čítače nebo dobře známé čítače. Další informace naleznete v tématu Přizpůsobení čítačů, které se mají shromažďovat.

dotnet-monitor

Tento dotnet-monitor nástroj usnadňuje přístup k diagnostice z procesu .NET vzdáleným a automatizovaným způsobem. 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 najdete v tématu dotnet-monitor.

Využití v proc

Hodnoty čítačů můžete využívat prostřednictvím EventListener rozhraní API. Jedná se EventListener o proc 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.

EventSource Nejprve je potřeba povolit hodnotu čítače. Přepište metodu EventListener.OnEventSourceCreated , aby se při EventSource vytvoření zobrazilo oznámení, a pokud je to správná EventSource hodnota u 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ázkový kód

Zde 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ždou sekundu.

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 uvedeno výše, je nutné zajistit, aby "EventCounterIntervalSec" byl 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é