EventCounters a .NET-ben
Ez a cikk a következőre vonatkozik: ✔️ .NET Core 3.0 SDK és újabb verziók
Feljegyzés
Az új .NET-projektek fejlesztéséhez a Microsoft az újabb System.Diagnostics.Metrics API-kat javasolja. A System.Diagnostics.Metrics API-k nagyobb funkcionalitást, szabványosítást és integrációt kínálnak az eszközök szélesebb körű ökoszisztémájával. További információkért tekintse meg a Metrics API összehasonlítását .
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 .NET-jól ismert EventCounters szolgáltatásában.
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.
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ékeket1
,2
és5
a futó összeg8
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 EventSource szolgáltatóként közzétett mintát "Sample.EventCounter.Minimal"
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 Commandhívja megEventCommand.Enable
: . Ha csak abban az null
esetben 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 long
használat _requestCount
megszakadt olvasásának megakadályozása (32 bites architektúrákonInterlocked.Read).
_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.
Azure Application Insights
Az EventCounterst az Azure Monitor, pontosabban Azure-alkalmazás Insights használhatja. 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, EnableEvents 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.