Metrikák létrehozása
Ez a cikk a .NET Core 6 és újabb verzióinak ✔️✔️ .NET-keretrendszer 4.6.1-s és újabb verzióira vonatkozik
A .NET-alkalmazások a System.Diagnostics.Metrics API-k segítségével rendszerezhetők a fontos metrikák nyomon követéséhez. Egyes metrikák a standard .NET-kódtárakban találhatók, de érdemes lehet új egyéni metrikákat hozzáadni, amelyek relevánsak az alkalmazásokhoz és tárakhoz. Ebben az oktatóanyagban új metrikákat fog hozzáadni, és megismerheti, hogy milyen típusú metrikák érhetők el.
Jegyzet
A .NET rendelkezik néhány régebbi metrika API-val, nevezetesen EventCounters és System.Diagnostics.PerformanceCounter, amelyek itt nem szerepelnek. További információ ezekről az alternatívákról: Metrika API-k összehasonlítása.
Egyéni metrikák létrehozása
előfeltételek: .NET Core 6 SDK vagy újabb verziója
Hozzon létre egy új konzolalkalmazást, amely a System.Diagnostics.DiagnosticSource NuGet-csomagra hivatkozik, 8-os vagy újabb verzióját. A .NET 8+ célalkalmazások alapértelmezés szerint tartalmazzák ezt a hivatkozást. Ezután frissítse a kódot a Program.cs
-ben, hogy megfeleljen:
> dotnet new console
> dotnet add package System.Diagnostics.DiagnosticSource
using System;
using System.Diagnostics.Metrics;
using System.Threading;
class Program
{
static Meter s_meter = new Meter("HatCo.Store");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>("hatco.store.hats_sold");
static void Main(string[] args)
{
Console.WriteLine("Press any key to exit");
while(!Console.KeyAvailable)
{
// Pretend our store has a transaction each second that sells 4 hats
Thread.Sleep(1000);
s_hatsSold.Add(4);
}
}
}
A System.Diagnostics.Metrics.Meter típus a kódtár belépési pontja egy elnevezett eszközcsoport létrehozásához. A műszerek rögzítik a metrikák kiszámításához szükséges numerikus méréseket. Itt a CreateCounter-t használtuk, hogy egy "hatco.store.hats_sold" nevű számlálóeszközt hozzunk létre. Minden szimulált tranzakció során a kód meghívja a Add függvényt, hogy rögzítse az eladott kalapok számát, ebben az esetben 4-et. A "hatco.store.hats_sold" eszköz implicit módon definiál néhány metrikát, amelyek ezekből a mérésekből számíthatók ki, például az eladott kalapok vagy az eladott kalapok száma másodpercenként. Végső soron a metrikagyűjtési eszközökön múlik annak meghatározása, hogy mely metrikákat kell kiszámítani, és hogyan kell elvégezni ezeket a számításokat, de mindegyik eszköz rendelkezik néhány alapértelmezett konvencióval, amelyek a fejlesztő szándékát közvetítik. Számlálóeszközök esetében az a konvenció, hogy a gyűjtőeszközök a teljes darabszámot és/vagy a darabszám növelésének sebességét mutatják.
A Counter<int>
és CreateCounter<int>(...)
általános paramétere, a int
, meghatározza, hogy ennek a számlálónak képesnek kell lennie az értékek Int32.MaxValuetárolására. A byte
, short
, int
, long
, float
, double
vagy decimal
bármelyikét használhatja az adatok méretétől és a törtértékek szükségességétől függően.
Futtassa az alkalmazást, és hagyja futni egy darabig. A metrikákat a következő lépésben tekintjük meg.
> dotnet run
Press any key to exit
Ajánlott eljárások
Az olyan kódok esetében, amelyek nincsenek függőséginjektálási (DI) tároló használatára tervezve, a mérőt egyszer létre kell hozni, és egy statikus változóban kell tárolni. A DI-tudatos kódtárakban való használat esetén a statikus változók antimintának minősülnek, és az alábbi DI-példa egy idiomatikusabb megközelítést mutat. Az egyes kódtáraknak vagy kódtár-alösszetevőknek saját Meter-t kell létrehozniuk (és gyakran kellene is). Fontolja meg egy új mérőszám létrehozását ahelyett, hogy újrahasználná a meglévőt, ha azt szeretné, hogy az alkalmazásfejlesztők könnyen engedélyezhessék és letilthassák a metrikák csoportjait.
A Meter konstruktornak átadott névnek egyedinek kell lennie, hogy megkülönböztesse a többi mérőtől. Javasoljuk az OpenTelemetry elnevezési irányelveit, amelyek pontokkal tagolt hierarchikus neveket használnak. Az instrumentált kódhoz tartozó szerelvénynevek vagy névterek általában jó választásnak számítanak. Ha egy összeállítás egy második, független összeállítás számára ad hozzá műszerezést a kódhoz, a névnek a Mérőt meghatározó összeállításon kell alapulnia, nem pedig azon az összeállításon, amelynek a kódját műszerezik.
A .NET nem kényszeríti ki az eszközök elnevezési sémáját, de javasoljuk, hogy kövesse OpenTelemetria elnevezési irányelveit, amelyek kisbetűs pontozott hierarchikus neveket és aláhúzásjelet ('_') használnak elválasztóként több szó között ugyanabban az elemben. Nem minden metrikaeszköz őrzi meg a mérőszám nevét a végső metrikanév részeként, ezért előnyös, ha a műszer nevét önmagában globálisan egyedivé teszi.
Példa eszköznevekre:
contoso.ticket_queue.duration
contoso.reserved_tickets
contoso.purchased_tickets
A műszerek létrehozásához és a mérések rögzítéséhez használható API-k szálbiztosak. A .NET-kódtárakban a legtöbb példánymetódus szinkronizálást igényel, ha ugyanazon az objektumon több szálból hívják meg, de ebben az esetben erre nincs szükség.
A mérések rögzítésére szolgáló eszköz API-k (Add ebben a példában) általában <10 ns-ben futnak, amikor nem gyűjtenek adatokat, vagy több tíz-száz nanoszekundumot, amikor a méréseket nagy teljesítményű gyűjteménytár vagy eszköz gyűjti össze. Ez lehetővé teszi ezeknek az API-knak a használatát a legtöbb esetben, de ügyeljen a rendkívül teljesítményérzékeny kódokra.
Az új metrika megtekintése
A metrikák tárolására és megtekintésére számos lehetőség áll rendelkezésre. Ez az oktatóanyag a dotnet-counters eszközt használja, amely alkalmi elemzésekhez hasznos. A metrikák gyűjteményének oktatóanyagát is megtekintheti, más alternatívákhoz. Ha a dotnet-counters eszköz még nincs telepítve, telepítse az SDK-val:
> dotnet tool update -g dotnet-counters
You can invoke the tool using the following command: dotnet-counters
Tool 'dotnet-counters' (version '7.0.430602') was successfully installed.
Amíg a példaalkalmazás még fut, a dotnet-counters használatával figyelje az új számlálót:
> dotnet-counters monitor -n metric-demo.exe --counters HatCo.Store
Press p to pause, r to resume, q to quit.
Status: Running
[HatCo.Store]
hatco.store.hats_sold (Count / 1 sec) 4
Ahogy várható volt, láthatja, hogy a HatCo áruház másodpercenként 4 kalapot értékesít.
Mérő lekérése függőséginjektálással
Az előző példában a mérőt úgy kapták meg, hogy new
létrehozásával, majd egy statikus mezőhöz rendelték. A statikusok ily módon történő használata nem jó módszer függőséginjektálás (DI) használatakor. A DI-t használó kódban, például a ASP.NET Core-ban vagy az általános gazdagép
Hogy egy DI-hez tervezett típusban mérőt kapjon, adjon hozzá egy IMeterFactory paramétert a konstruktorhoz, majd hívja meg a Create-et. Ez a példa az IMeterFactory használatát mutatja be egy ASP.NET Core-alkalmazásban.
Adjon meg egy típust a műszerek tárolásához:
public class HatCoMetrics
{
private readonly Counter<int> _hatsSold;
public HatCoMetrics(IMeterFactory meterFactory)
{
var meter = meterFactory.Create("HatCo.Store");
_hatsSold = meter.CreateCounter<int>("hatco.store.hats_sold");
}
public void HatsSold(int quantity)
{
_hatsSold.Add(quantity);
}
}
Regisztrálja a típust a Program.cs
DI-konténerben.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<HatCoMetrics>();
Szükség esetén adja meg a metrikák típusát és rekordértékeit. Mivel a metrikák típusa regisztrálva van a DI-ben, MVC-vezérlőkkel, minimális API-kkal vagy bármely más, a DI által létrehozott típussal használható:
app.MapPost("/complete-sale", ([FromBody] SaleModel model, HatCoMetrics metrics) =>
{
// ... business logic such as saving the sale to a database ...
metrics.HatsSold(model.QuantitySold);
});
Ajánlott eljárások
-
System.Diagnostics.Metrics.Meter implementálja az IDisposable-t, de az IMeterFactory automatikusan kezeli az általa létrehozott
Meter
objektumok élettartamát, és a DI-tároló ártalmatlanításakor elosztja őket. Nem szükséges további kódot hozzáadni aMeter
-hez aDispose()
meghívásához, és ennek nem lesz semmilyen hatása.
Eszközök típusai
Eddig csak egy Counter<T> hangszert mutattunk be, de több eszköztípus érhető el. Az eszközök kétféleképpen különböznek:
- Alapértelmezett metrikaszámítások – A műszeres méréseket gyűjtő és elemző eszközök a műszertől függően különböző alapértelmezett metrikákat fognak kiszámítani.
- Összesített adatok tárolása – A legtöbb hasznos metrikához számos mérésből összesíteni kell az adatokat. Az egyik lehetőség az, hogy a hívó tetszőleges időpontokban biztosít egyéni méréseket, és a gyűjtőeszköz kezeli az összesítést. Másik lehetőségként a hívó kezelheti az összesített méréseket, és igény szerint biztosíthatja őket egy visszahívásban.
Jelenleg elérhető eszközök típusai:
Számláló (CreateCounter) – Ez az eszköz nyomon követi az idő múlásával növekvő értéket, és a hívó az Addhasználatával jelenti a növekményeket. A legtöbb eszköz kiszámítja az összeg és a változás mértékét az összegben. Az olyan eszközök esetében, amelyek csak egy dolgot mutatnak, a változás sebessége ajánlott. Tegyük fel például, hogy a hívó másodpercenként egyszer hívja meg
Add()
egymást követő értékekkel: 1, 2, 4, 5, 4, 3. Ha a gyűjtési eszköz három másodpercenként frissül, akkor a három másodperc utáni összeg 1+2+4=7, a hat másodperc után pedig 1+2+4+5+4+3=19. A változás sebessége a (current_total - previous_total), így három másodpercen belül az eszköz 7-0=7-et, hat másodperc után pedig 19-7=12-t jelent.UpDownCounter (CreateUpDownCounter) – Ez az eszköz egy értéket követ nyomon, amely az idő múlásával növekedhet vagy csökkenhet. A hívó a Addhasználatával jelenti a növekményeket és a hanyatlásokat. Tegyük fel például, hogy a hívó másodpercenként egyszer hívja meg
Add()
egymást követő 1, 5, -2, 3, -1, -3 értékekkel. Ha a gyűjtési eszköz három másodpercenként frissül, akkor a három másodperc utáni összeg 1+5-2=4, a hat másodperc után pedig 1+5-2+3-1-3=3.ObservableCounter (CreateObservableCounter) – Ez az eszköz hasonló a Számlálóhoz, azzal a kivételével, hogy a hívó felelős az összesített összeg fenntartásáért. A hívó egy visszahívási meghatalmazottat biztosít az ObservableCounter létrehozásakor, és a visszahívás akkor lesz meghívva, amikor az eszközöknek meg kell figyelnie az aktuális végösszeget. Ha például egy gyűjteményeszköz három másodpercenként frissül, akkor a visszahívási függvény is három másodpercenként lesz meghívva. A legtöbb eszköznél elérhető a teljes érték és a változás mértéke is. Ha csak egy látható, a változás sebessége ajánlott. Ha a visszahívás 0-t ad vissza az első híváskor, 7-et, amikor három másodperc után ismét meghívják, hat másodperc után pedig 19-et, akkor az eszköz ezeket az értékeket összegként változatlanul jelenti. A változási sebességnél az eszköz három másodperc után 7-0=7, hat másodperc után pedig 19-7=12-t jelenít meg.
ObservableUpDownCounter (CreateObservableUpDownCounter) – Ez az eszköz hasonló az UpDownCounterhez, azzal a kivételével, hogy a hívó felelős az összesített összeg fenntartásáért. A hívó egy visszahívási meghatalmazottat biztosít az ObservableUpDownCounter létrehozásakor, és a visszahívást minden alkalommal meghívja, amikor az eszközöknek meg kell figyelnie az aktuális végösszeget. Ha például egy gyűjteményeszköz három másodpercenként frissül, akkor a visszahívási függvény is három másodpercenként lesz meghívva. Bármilyen értéket ad vissza a visszahívás, az összegként változatlanul jelenik meg a gyűjteményeszközben.
Mérőműszer (CreateGauge) – Ez a műszer lehetővé teszi a hívó számára a metrika aktuális értékének beállítását a Record metódus használatával. Az érték bármikor frissíthető a metódus ismételt meghívásával, és a metrikagyűjtő eszköz megjeleníti a legutóbb beállított értéket.
ObservableGauge (CreateObservableGauge) – Ez az eszköz lehetővé teszi a hívó számára, hogy visszahívást biztosítson, ahol a mért érték közvetlenül metrikaként lesz átadva. Minden alkalommal, amikor a gyűjteményeszköz frissül, a visszahívás meghívódik, és bármilyen értéket ad vissza a visszahívás, azt jeleníti meg az eszköz.
hisztogram (CreateHistogram) – Ez a műszer nyomon követi a mérések eloszlását. A mérések egy csoportjának leírására nincs egyetlen canonikus módszer, de ajánlott hisztogramokat vagy számított percentiliseket használni. Tegyük fel például, hogy a hívó meghívta Record, hogy rögzítse ezeket a méréseket a gyűjtőeszköz frissítési időköze alatt: 1,5,2,3,10,9,7,4,6,8. A gyűjtőeszköz jelentheti, hogy a mérések 50., 90. és 95. percentilisei 5, 9 és 9.
Jegyzet
A Hisztogram eszköz létrehozásakor ajánlott gyűjtőhatárok beállításáról a következő cikkben talál információt: Tanácsok a hisztogram eszközök testreszabásához.
Ajánlott eljárások egy eszköztípus kiválasztásakor
Az értékek számolása esetén, amelyek idővel folyamatosan növekednek, használja a Számlálót vagy az ObservableCountert. Válasszon a Counter és az ObservableCounter között, attól függően, hogy melyiket lehet egyszerűbben hozzáadni a meglévő kódhoz: egy API-hívás minden növekményes művelethez, vagy egy visszahívás, amely beolvassa az aktuális végösszeget a kód által karbantartott változóból. A rendkívül gyakori kódelérési utakon, ahol a teljesítmény fontos, és a Add használata másodpercenként több mint egymillió hívást hozna létre szálonként, az ObservableCounter használata több lehetőséget kínálhat az optimalizálásra.
Az időzítéshez általában a hisztogramot részesítik előnyben. Gyakran hasznos megérteni ezeknek a eloszlásoknak a farkát (90., 95., 99. percentilis) az átlagok és az összegek helyett.
Más gyakori esetek, mint például a gyorsítótár találati aránya, vagy a gyorsítótárak, üzenetsorok és fájlok mérete, általában jól illeszkednek a
UpDownCounter
vagy aObservableUpDownCounter
kategóriákhoz. Válasszon közülük attól függően, hogy melyiket lehet egyszerűbben hozzáadni a meglévő kódhoz: az egyes növekményes és csökkenő műveletekhez tartozó API-hívásokat, vagy egy visszahívást, amely beolvassa az aktuális értéket a kód által karbantartott változóból.
Jegyzet
Ha a .NET régebbi verzióját vagy olyan DiagnosticSource NuGet-csomagot használ, amely nem támogatja UpDownCounter
és ObservableUpDownCounter
(a 7. verzió előtt), a ObservableGauge
gyakran jó helyettesítője.
Példa a különböző eszköztípusokra
Állítsa le a korábban elindított példafolyamatot, és cserélje le a példakódot Program.cs
a következőre:
using System;
using System.Diagnostics.Metrics;
using System.Threading;
class Program
{
static Meter s_meter = new Meter("HatCo.Store");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>("hatco.store.hats_sold");
static Histogram<double> s_orderProcessingTime = s_meter.CreateHistogram<double>("hatco.store.order_processing_time");
static int s_coatsSold;
static int s_ordersPending;
static Random s_rand = new Random();
static void Main(string[] args)
{
s_meter.CreateObservableCounter<int>("hatco.store.coats_sold", () => s_coatsSold);
s_meter.CreateObservableGauge<int>("hatco.store.orders_pending", () => s_ordersPending);
Console.WriteLine("Press any key to exit");
while(!Console.KeyAvailable)
{
// Pretend our store has one transaction each 100ms that each sell 4 hats
Thread.Sleep(100);
s_hatsSold.Add(4);
// Pretend we also sold 3 coats. For an ObservableCounter we track the value in our variable and report it
// on demand in the callback
s_coatsSold += 3;
// Pretend we have some queue of orders that varies over time. The callback for the orders_pending gauge will report
// this value on-demand.
s_ordersPending = s_rand.Next(0, 20);
// Last we pretend that we measured how long it took to do the transaction (for example we could time it with Stopwatch)
s_orderProcessingTime.Record(s_rand.Next(5, 15)/1000.0);
}
}
}
Futtassa az új folyamatot, és a metrikák megtekintéséhez használja a dotnet-counterst, mint korábban egy második parancssorban.
> dotnet-counters monitor -n metric-demo.exe --counters HatCo.Store
Press p to pause, r to resume, q to quit.
Status: Running
Name Current Value
[HatCo.Store]
hatco.store.coats_sold (Count) 8,181
hatco.store.hats_sold (Count) 548
hatco.store.order_processing_time
Percentile
50 0.012
95 0.013
99 0.013
hatco.store.orders_pending 9
Ez a példa véletlenszerűen generált számokat használ, így az értékek kissé eltérőek lesznek. A Dotnet-counters három percentilis statisztikai adatként jeleníti meg a hisztogram-eszközöket (50., 95. és 99.), de más eszközök másképpen összegzik a eloszlást, vagy további konfigurációs lehetőségeket kínálnak.
Ajánlott eljárások
A hisztogramok általában sokkal több adatot tárolnak a memóriában, mint más metrikatípusok. A pontos memóriahasználatot azonban a használt gyűjteményeszköz határozza meg. Ha nagy számú (>100) hisztogrammetrikát határoz meg, előfordulhat, hogy útmutatást kell adnia a felhasználóknak ahhoz, hogy ne engedélyezze őket egyszerre, vagy hogy az eszközeiket úgy konfigurálják, hogy a pontosság csökkentésével memóriát takaríthassanak meg. Előfordulhat, hogy egyes gyűjteményeszközök korlátozottan korlátozzák az egyidejű hisztogramok számát, amelyeket figyelni fognak a túlzott memóriahasználat megakadályozása érdekében.
A rendszer sorrendben hívja meg az összes megfigyelhető eszköz visszahívásait, így a hosszú ideig tartó visszahívások késleltethetik vagy megakadályozhatják az összes metrika gyűjtését. A gyorsítótárazott érték gyors olvasását, a mérések nélküli visszatérést vagy kivétel dobását részesítse előnyben a potenciálisan hosszan tartó vagy blokkoló műveletek végrehajtásával szemben.
Az ObservableCounter, az ObservableUpDownCounter és az ObservableGauge visszahívások olyan szálon fordulnak elő, amely általában nem szinkronizálva van az értékeket frissíteni kívánt kóddal. Az Ön felelőssége, hogy szinkronizálja a memóriahozzáférést, vagy elfogadja a nem aszinkron hozzáférésből eredő inkonzisztens értékeket. A hozzáférés szinkronizálásának gyakori módszere a zárolás vagy a hívás Volatile.Read és Volatile.Writehasználata.
A CreateObservableGauge és a CreateObservableCounter függvények egy eszközobjektumot adnak vissza, de a legtöbb esetben nem kell változóba menteni, mert nincs szükség további interakcióra az objektummal. Ha statikus változóhoz rendeljük, mint a többi eszköz esetében, az jogi, de hibalehetőséget jelent, mivel a C# statikus inicializálása lusta, és a változóra általában soha nem hivatkozunk. Íme egy példa a problémára:
using System; using System.Diagnostics.Metrics; class Program { // BEWARE! Static initializers only run when code in a running method refers to a static variable. // These statics will never be initialized because none of them were referenced in Main(). // static Meter s_meter = new Meter("HatCo.Store"); static ObservableCounter<int> s_coatsSold = s_meter.CreateObservableCounter<int>("hatco.store.coats_sold", () => s_rand.Next(1,10)); static Random s_rand = new Random(); static void Main(string[] args) { Console.ReadLine(); } }
Leírások és egységek
Az eszközök megadhatnak opcionális leírásokat és egységeket. Ezek az értékek átlátszatlanok az összes metrikaszámításhoz, de a gyűjtőeszköz felhasználói felületén is megjeleníthetők, így a mérnökök könnyebben megérthetik az adatok értelmezését. Állítsa le a korábban elindított példafolyamatot, és cserélje le a példakódot a Program.cs
a következőre:
using System;
using System.Diagnostics.Metrics;
using System.Threading;
class Program
{
static Meter s_meter = new Meter("HatCo.Store");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>(name: "hatco.store.hats_sold",
unit: "{hats}",
description: "The number of hats sold in our store");
static void Main(string[] args)
{
Console.WriteLine("Press any key to exit");
while(!Console.KeyAvailable)
{
// Pretend our store has a transaction each 100ms that sells 4 hats
Thread.Sleep(100);
s_hatsSold.Add(4);
}
}
}
Futtassa az új folyamatot, és a metrikák megtekintéséhez használja a dotnet-counters eszközt egy második rendszerhéjban, ahogyan korábban.
Press p to pause, r to resume, q to quit.
Status: Running
Name Current Value
[HatCo.Store]
hatco.store.hats_sold ({hats}) 40
A dotnet-counters jelenleg nem használja a leírás szövegét a felhasználói felületen, de megjeleníti az egységet, amikor meg van adva. Ebben az esetben a "{hats}" kifejezés a korábbi leírásokban látható általános "Darabszám" kifejezést váltotta fel.
Ajánlott eljárások
A .NET API-k lehetővé teszik, hogy bármilyen sztringet használjon egységként, de javasoljuk a UCUMhasználatát, amely az egységek neveinek nemzetközi szabványa. A(z) "{hats}" körüli kapcsos zárójelek az UCUM szabvány részét képezik, ami azt jelzi, hogy ez egy leíró széljegyzet, nem pedig egy egységnév, amely szabványosított jelentést, például másodpercet vagy bájtot jelent.
A konstruktorban megadott egységnek le kell írnia az egyes mérésekhez megfelelő egységeket. Ez néha eltér a végleges jelentett metrika egységeitől. Ebben a példában minden mérés több kalapból áll, így a "{hats}" a konstruktoron áthaladó megfelelő egység. A gyűjtési eszköz kiszámíthatta volna a változás sebességét, és önmagában is kiszámíthatta volna, hogy a számított sebességmetrika megfelelő mértékegysége {hats}/sec.
Az időmérések rögzítésekor előnyben részesítse a lebegőpontos vagy kettős értékként rögzített másodpercegységeket.
Többdimenziós metrikák
A mérések olyan kulcs-érték párokhoz is társíthatók, amelyeket címkéknek neveznek, amelyek lehetővé teszik az adatok elemzésére való kategorizálását. Előfordulhat például, hogy a HatCo nem csak az eladott kalapok számát szeretné rögzíteni, hanem azt is, hogy milyen méretűek és színűek voltak. Az adatok későbbi elemzésekor a HatCo mérnökei méret, szín vagy a kettő bármilyen kombinációja alapján bonthatják ki az összegeket.
A számláló- és hisztogramcímkék megadhatók a Add és Record túlterheléseiben, amelyek egy vagy több KeyValuePair
argumentumot fogadnak el. Például:
s_hatsSold.Add(2,
new KeyValuePair<string, object?>("product.color", "red"),
new KeyValuePair<string, object?>("product.size", 12));
Cserélje le a Program.cs
kódját, és futtassa újra az alkalmazást és a dotnet-counterst a korábbiakhoz hasonlóan:
using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Threading;
class Program
{
static Meter s_meter = new Meter("HatCo.Store");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>("hatco.store.hats_sold");
static void Main(string[] args)
{
Console.WriteLine("Press any key to exit");
while(!Console.KeyAvailable)
{
// Pretend our store has a transaction, every 100ms, that sells two size 12 red hats, and one size 19 blue hat.
Thread.Sleep(100);
s_hatsSold.Add(2,
new KeyValuePair<string,object?>("product.color", "red"),
new KeyValuePair<string,object?>("product.size", 12));
s_hatsSold.Add(1,
new KeyValuePair<string,object?>("product.color", "blue"),
new KeyValuePair<string,object?>("product.size", 19));
}
}
}
A Dotnet-counters mostantól egy alapszintű kategorizálást jelenít meg:
Press p to pause, r to resume, q to quit.
Status: Running
Name Current Value
[HatCo.Store]
hatco.store.hats_sold (Count)
product.color product.size
blue 19 73
red 12 146
Az ObservableCounter és az ObservableGauge esetében címkézett méréseket a konstruktornak átadott visszahívásban lehet megadni:
using System;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Threading;
class Program
{
static Meter s_meter = new Meter("HatCo.Store");
static void Main(string[] args)
{
s_meter.CreateObservableGauge<int>("hatco.store.orders_pending", GetOrdersPending);
Console.WriteLine("Press any key to exit");
Console.ReadLine();
}
static IEnumerable<Measurement<int>> GetOrdersPending()
{
return new Measurement<int>[]
{
// pretend these measurements were read from a real queue somewhere
new Measurement<int>(6, new KeyValuePair<string,object?>("customer.country", "Italy")),
new Measurement<int>(3, new KeyValuePair<string,object?>("customer.country", "Spain")),
new Measurement<int>(1, new KeyValuePair<string,object?>("customer.country", "Mexico")),
};
}
}
Ha a korábbiakhoz hasonlóan dotnet-számlálókkal fut, az eredmény a következő:
Press p to pause, r to resume, q to quit.
Status: Running
Name Current Value
[HatCo.Store]
hatco.store.orders_pending
customer.country
Italy 6
Mexico 1
Spain 3
Ajánlott eljárások
Bár az API lehetővé teszi, hogy bármilyen objektumot használjon címkeértékként, a gyűjteményeszközök numerikus típusokat és sztringeket várnak. Más típusokat egy adott gyűjtési eszköz támogathat vagy nem.
Javasoljuk, hogy a címkék nevei az OpenTelemetry elnevezési irányelveitkövetik, amelyek kisbetűs pontozott hierarchikus neveket használnak "_" karakterekkel, hogy több szót különítsenek el ugyanabban az elemben. Ha a címkeneveket különböző metrikákban vagy más telemetriai rekordokban használják újra, akkor mindenhol azonos jelentéssel és jogi értékekkel kell rendelkezniük.
Példacímkék nevei:
customer.country
store.payment_method
store.purchase_result
Ügyeljen arra, hogy a gyakorlatban ne legyenek nagyon nagy vagy korlátlan címkeértékek rögzítve. Bár a .NET API implementációja képes kezelni, a gyűjtési eszközök valószínűleg lefoglalják az egyes címkekombinációkhoz társított metrikaadatok tárolását, és ez nagyon nagy méretűvé válhat. Jó például, ha a HatCo 10 különböző kalapszínt és 25 kalapméretet biztosít akár 10*25=250 értékesítési összeg nyomon követéséhez. Ha azonban a HatCo hozzáad egy harmadik címkét, amely ügyfélazonosító az értékesítéshez, és világszerte 100 millió ügyfélnek adnak el, most valószínűleg több milliárd különböző címkekombinációt rögzítenek. A legtöbb metrikagyűjtő eszköz vagy elveti az adatokat, hogy a technikai korlátokon belül maradjon, vagy nagy pénzbeli költségekkel jár az adattárolás és -feldolgozás. Az egyes gyűjtési eszközök megvalósítása meghatározza a korlátait, de egy eszköz esetében valószínűleg kevesebb mint 1000 kombináció biztonságos. Az 1000-nél több kombináció esetén a gyűjtőeszköznek szűrést kell alkalmaznia, vagy nagy léptékű működésre kell terveznie. A hisztogram implementációk általában sokkal több memóriát használnak, mint más metrikák, így a biztonságos korlátok 10-100-szor alacsonyabbak lehetnek. Ha nagy számú egyedi címkekombinációra számít, akkor a naplók, a tranzakciós adatbázisok vagy a big data feldolgozó rendszerek megfelelőbb megoldások lehetnek a szükséges léptékben való működéshez.
Az olyan eszközök esetében, amelyek nagyon sok címkekombinációval rendelkeznek, inkább használjon kisebb tárolási típust a memóriaterhelés csökkentéséhez. Egy
Counter<short>
short
tárolása például csak 2 bájtot foglal el címkekombinációnként, míg aCounter<double>
double
címkekombinációnként 8 bájtot foglal el.A gyűjtőeszközöket arra javasoljuk, hogy olyan kódra optimalizáljanak, amely ugyanazt a címkenevet adja meg ugyanabban a sorrendben az egyes hívásokhoz, hogy ugyanazon a készüléken rögzíthessék a méréseket. Az Add és Record gyakran hívandó nagy teljesítményű kódok esetében érdemes ugyanazt a címkeneveket használni minden híváshoz.
A .NET API úgy van optimalizálva, hogy kiosztásmentes legyen Add és Record hívásokhoz, egyenként három vagy kevesebb címkével. A nagyobb számú címkével rendelkező foglalások elkerülése érdekében használja a TagList. Általánosságban elmondható, hogy a hívások teljesítményterhelése nő, mivel több címkét használnak.
Jegyzet
Az OpenTelemetria attribútumként hivatkozik a címkékre. Ezek két különböző nevet jelentenek ugyanahhoz a funkcióhoz.
Tanácsok használata a hisztogram-eszközök testreszabásához
Hisztogramok használata esetén az adatokat gyűjtő eszköz vagy tár feladata eldönteni, hogy a legjobban hogyan ábrázolja a rögzített értékek eloszlását. Az OpenTelemetryhasználatakor az általános stratégia (és az
A hisztogramadatokat gyűjtő eszköz vagy kódtár felelős a használni kívánt gyűjtők meghatározásáért. Az OpenTelemetria használatakor az alapértelmezett gyűjtőkonfiguráció a következő: [ 0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000 ]
.
Előfordulhat, hogy az alapértelmezett értékek nem minden hisztogram esetében a legjobb részletességet eredményezik. Például a másodpercnél rövidebb kérések időtartama mind a 0
gyűjtőbe kerülnének.
A hisztogramadatokat gyűjtő eszköz vagy kódtár olyan mechanizmusokat kínálhat, amelyekkel a felhasználók testre szabhatják a gyűjtőkonfigurációt. Az OpenTelemetry például egy View API-definiál. Ehhez azonban végfelhasználói műveletre van szükség, és a felhasználó felelőssége, hogy a megfelelő gyűjtők kiválasztásához elég jól megértse az adatelosztást.
A felhasználói élmény javítása érdekében a System.Diagnostics.DiagnosticSource
csomag 9.0.0
verziója bevezette a (InstrumentAdvice<T>) API-t.
A InstrumentAdvice
API-t a rendszerállapot-készítők használhatják egy adott hisztogram ajánlott alapértelmezett gyűjtőhatárainak megadásához. A hisztogramadatokat gyűjtő eszköz vagy kódtár ezután dönthet úgy, hogy ezeket az értékeket használja az összesítés konfigurálásakor, ami zökkenőmentesebb előkészítési élményt eredményez a felhasználók számára. Ezt a OpenTelemetry .NET SDK támogatja az 1.10.0-s verziótól.
Fontos
Általánosságban elmondható, hogy a több gyűjtő pontosabb adatokat eredményez egy adott hisztogramhoz, de minden gyűjtőnek memóriára van szüksége az összesített adatok tárolásához, és a mérés feldolgozásakor processzorköltséggel kell megkeresni a megfelelő gyűjtőt. Az InstrumentAdvice
API-n keresztül javasolt gyűjtők számának kiválasztásakor fontos tisztában lenni a pontosság és a processzor/memóriahasználat közötti kompromisszumokkal.
Az alábbi kód egy példát mutat be a InstrumentAdvice
API használatával az ajánlott alapértelmezett rekeszek beállításához.
using System;
using System.Diagnostics.Metrics;
using System.Threading;
class Program
{
static Meter s_meter = new Meter("HatCo.Store");
static Histogram<double> s_orderProcessingTime = s_meter.CreateHistogram<double>(
name: "hatco.store.order_processing_time",
unit: "s",
description: "Order processing duration",
advice: new InstrumentAdvice<double> { HistogramBucketBoundaries = [0.01, 0.05, 0.1, 0.5, 1, 5] });
static Random s_rand = new Random();
static void Main(string[] args)
{
Console.WriteLine("Press any key to exit");
while (!Console.KeyAvailable)
{
// Pretend our store has one transaction each 100ms
Thread.Sleep(100);
// Pretend that we measured how long it took to do the transaction (for example we could time it with Stopwatch)
s_orderProcessingTime.Record(s_rand.Next(5, 15) / 1000.0);
}
}
}
További információk
Az OpenTelemetry explicit gyűjtő hisztogramjairól további részletekért lásd:
Egyéni metrikák tesztelése
A MetricCollector<T>használatával hozzáadott egyéni metrikák tesztelésére is lehetősége van. Ez a típus megkönnyíti az egyes műszerek méréseinek rögzítését és az értékek helyességét.
Tesztelés függőséginjektálással
Az alábbi kód egy példatesztes esetet mutat be a függőséginjektálást és az IMeterFactory-t használó kódösszetevők esetében.
public class MetricTests
{
[Fact]
public void SaleIncrementsHatsSoldCounter()
{
// Arrange
var services = CreateServiceProvider();
var metrics = services.GetRequiredService<HatCoMetrics>();
var meterFactory = services.GetRequiredService<IMeterFactory>();
var collector = new MetricCollector<int>(meterFactory, "HatCo.Store", "hatco.store.hats_sold");
// Act
metrics.HatsSold(15);
// Assert
var measurements = collector.GetMeasurementSnapshot();
Assert.Equal(1, measurements.Count);
Assert.Equal(15, measurements[0].Value);
}
// Setup a new service provider. This example creates the collection explicitly but you might leverage
// a host or some other application setup code to do this as well.
private static IServiceProvider CreateServiceProvider()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddMetrics();
serviceCollection.AddSingleton<HatCoMetrics>();
return serviceCollection.BuildServiceProvider();
}
}
Minden MetricCollector-objektum egy eszköz összes mérését rögzíti. Ha több mérőműszer méréseit kell ellenőriznie, mindegyikhez hozzon létre egy MetricCollectort.
Tesztelés függőségi injektálás nélkül
A statikus mezőben megosztott globális mérőobjektumot használó kódot is tesztelhet, de győződjön meg arról, hogy az ilyen tesztek úgy vannak konfigurálva, hogy ne fussanak párhuzamosan. Mivel a Meter objektum meg van osztva, a MetricCollector egy tesztben megfigyeli a más, párhuzamosan futó tesztekből létrehozott méréseket.
class HatCoMetricsWithGlobalMeter
{
static Meter s_meter = new Meter("HatCo.Store");
static Counter<int> s_hatsSold = s_meter.CreateCounter<int>("hatco.store.hats_sold");
public void HatsSold(int quantity)
{
s_hatsSold.Add(quantity);
}
}
public class MetricTests
{
[Fact]
public void SaleIncrementsHatsSoldCounter()
{
// Arrange
var metrics = new HatCoMetricsWithGlobalMeter();
// Be careful specifying scope=null. This binds the collector to a global Meter and tests
// that use global state should not be configured to run in parallel.
var collector = new MetricCollector<int>(null, "HatCo.Store", "hatco.store.hats_sold");
// Act
metrics.HatsSold(15);
// Assert
var measurements = collector.GetMeasurementSnapshot();
Assert.Equal(1, measurements.Count);
Assert.Equal(15, measurements[0].Value);
}
}