Megosztás a következőn keresztül:


Metrikák létrehozása

Ez a cikk a következő verziókra vonatkozik: ✔️ .NET Core 6 és újabb verziók ✔️ .NET-keretrendszer 4.6.1 és újabb verziók

A .NET-alkalmazások az API-k segítségével rendszerezhetők a System.Diagnostics.Metrics 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.

Megjegyzés:

A .NET rendelkezik néhány régebbi metrika API-val, nevezetesen az EventCounters és System.Diagnostics.PerformanceCounteraz itt nem tárgyalt metrika API-kkal. 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ó

Hozzon létre egy új konzolalkalmazást, amely a System.Diagnostics.DiagnosticSource NuGet csomag 8- vagy újabb verziójára hivatkozik. A .NET 8+ célalkalmazások alapértelmezés szerint tartalmazzák ezt a hivatkozást. Ezután frissítse a kódot Program.cs a következőhöz:

> 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 egy kódtár belépési pontja, amely egy elnevezett eszközcsoportot hoz létre. A műszerek rögzítik a metrikák kiszámításához szükséges numerikus méréseket. Itt egy "hatco.store.hats_sold" nevű számlálóeszközt hoztunk CreateCounter létre. Minden egyes tranzakció során a kód meghívja Add az eladott kalapok mérésének rögzítését, ebben az esetben 4. 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.

Az általános paraméter int be van kapcsolva Counter<int> , és CreateCounter<int>(...) meghatározza, hogy ennek a számlálónak képesnek kell lennie az értékek legfeljebb Int32.MaxValue. Használhatja bármelyiket byte, short, , int, long, float, double, vagy decimal attól függően, hogy milyen méretű adatokat kell tárolnia, és hogy szükség van-e törtértékekre.

Futtassa az alkalmazást, és hagyja futni egyelőre. A metrikákat a következő lépésben tekintjük meg.

> dotnet run
Press any key to exit

Best practices

  • A nem függőséginjektálási (DI) tárolóban való használatra tervezett kódhoz hozza létre egyszer a mérőt, és tárolja egy statikus változóban. 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árak vagy kódtár-alösszetevők létrehozhatják (és gyakran kell) sajátokat Meterlétrehozniuk. 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 konstruktornak Meter átadott névnek egyedinek kell lennie, hogy megkülönböztesse a többi mérőtől. Az OpenTelemetria elnevezési irányelveit javasoljuk, amelyek pontozott hierarchikus neveket használnak. A rendszerezett kód szerelvénynevei vagy névterei általában jó választásnak számítanak. Ha egy szerelvény egy második, független szerelvényben adja hozzá a kód rendszerezését, a névnek a Mérőt meghatározó szerelvényen kell alapulnia, nem pedig azon a szerelvényen, amelynek a kódját a rendszerszerkesztette.

  • A .NET nem kényszeríti ki az eszközök elnevezési sémáját, de javasoljuk, hogy kövesse az 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 nanoszekundumban, amikor a méréseket nagy teljesítményű gyűjteménytár vagy -eszköz gyűjti. 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 ad-hoc elemzésekhez hasznos. A metrikák gyűjteményére vonatkozó oktatóanyagot más alternatívákhoz is megtekintheti. 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 szerezték be, hogy statikus mezővel new hozták létre és rendelték hozzá. 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épet használó alkalmazásokban hozza létre a Meter objektumot a használatávalIMeterFactory. A .NET 8-tól kezdve a gazdagépek automatikusan regisztrálnak IMeterFactory a szolgáltatástárolóban, vagy a típust bármelyikben IServiceCollection manuálisan regisztrálhatja a hívással AddMetrics. A mérőműszer-előállító integrálja a metrikákat a DI-vel, így a mérők különböző szolgáltatásgyűjteményekben vannak elkülönítve egymástól, még akkor is, ha azonos nevet használnak. Ez különösen hasznos a teszteléshez, hogy több párhuzamosan futó teszt csak az ugyanabban a tesztesetben előállított méréseket figyelje meg.

Ha egy DI-hez tervezett típusban szeretne mérőt beszerezni, adjon hozzá egy paramétert IMeterFactory a konstruktorhoz, majd hívja meg Create. 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 DI-tárolóval a következőben Program.cs: .

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

Best practices

  • System.Diagnostics.Metrics.Meter Implementálja az IDisposable-t, de az IMeterFactory automatikusan kezeli az általa létrehozott objektumok élettartamát Meter , és a DI-tároló ártalmatlanításakor megsemmisíti őket. Felesleges további kódot hozzáadni a meghíváshoz Dispose() , Meterés nem lesz hatása.

Eszközök típusai

Eddig csak egy hangszert Counter<T> 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 összegyűjtő és elemző eszközök a műszertől függően eltérő 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ó a növekményeket a használatával Addjelenti. 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ív meg Add() 1, 2, 4, 5, 4, 3 egymást követő é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+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 nyomon követ egy értéket, amely idővel növekedhet vagy csökkenhet. A hívó a növekményeket és a decrementációkat a használatával Addjelenti. Tegyük fel például, hogy a hívó másodpercenként egyszer hív meg Add() 1, 5, -2, 3, -1, -3 egymást követő é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 a Counterhoz hasonló, 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öz a teljes rendelkezésre álló összegben és a változási arányban is rendelkezik. 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 az UpDownCounterhez hasonló, 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.

  • 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 rendszer meghívja a visszahívást, és bármilyen értéket ad vissza a visszahívás az eszközben.

  • 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 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.

Ajánlott eljárások egy eszköztípus kiválasztásakor

  • A számláló vagy bármely más érték számlálásához, amely csak az idő múlásával növekszik, 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 használat Add szálonként másodpercenként több mint egymillió hívást eredményezne, 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, például a gyorsítótár-találatok aránya vagy a gyorsítótárak, az üzenetsorok és a fájlok méretei általában jól alkalmazhatók UpDownCounter vagy ObservableUpDownCounter. 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.

Megjegyzés:

Ha a .NET régebbi verzióját vagy (a 7-es verzió előtt) nem támogatott UpDownCounterObservableUpDownCounter DiagnosticSource NuGet-csomagot használ, ObservableGauge az gyakran jó helyettesítő.

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 a Program.cs 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(0.005, 0.015));
        }
    }
}

Futtassa az új folyamatot, és a metrikák megtekintéséhez használja a dotnet-counterst, mint korábban egy második rendszerhéjban:

> 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.coats_sold (Count / 1 sec)                                27
    hatco.store.hats_sold (Count / 1 sec)                                 36
    hatco.store.order_processing_time
        Percentile=50                                                      0.012
        Percentile=95                                                      0.014
        Percentile=99                                                      0.014
    hatco.store.orders_pending                                             5

Ez a példa véletlenszerűen generált számokat használ, így az értékek kissé eltérőek lesznek. Láthatja, hogy hatco.store.hats_sold (a Számláló) és hatco.store.coats_sold (az ObservableCounter) is arányként jelenik meg. Az ObservableGauge abszolút hatco.store.orders_pendingértékként jelenik meg. A Dotnet-counters három percentilis statisztikaké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.

Best practices

  • 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élyezzék egyszerre mindet, 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ának, a mérések nélküli visszatérésnek vagy a potenciálisan hosszú ideig futó vagy blokkoló műveletek végrehajtásának kivételének előnyben részesítése.

  • 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.Writea .

  • A CreateObservableGauge függvények CreateObservableCounter nem adnak vissza egy eszközobjektumot, 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 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-counterst, mint korábban egy második rendszerhéjban:

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

[HatCo.Store]
    hatco.store.hats_sold ({hats} / 1 sec)                                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.

Best practices

  • A .NET API-k lehetővé teszik, hogy bármilyen sztringet használjon egységként, de az egységnevek nemzetközi szabványa, a UCUM használatát javasoljuk. 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égső 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ított egy mértéket, és önmagában származtatott, hogy a számított metrika 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 túlterhelésekben, és Record egy vagy több KeyValuePair argumentumot vesznek igénybe. Például:

s_hatsSold.Add(2,
               new KeyValuePair<string, object>("product.color", "red"),
               new KeyValuePair<string, object>("product.size", 12));

Cserélje le és Program.cs futtassa újra az alkalmazás és a dotnet-counter kódját 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

[HatCo.Store]
    hatco.store.hats_sold (Count / 1 sec)
        product.color=blue,product.size=19                                 9
        product.color=red,product.size=12                                 18

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

[HatCo.Store]
    hatco.store.orders_pending
        customer.country=Italy                                             6
        customer.country=Mexico                                            1
        customer.country=Spain                                             3

Best practices

  • 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 neve kövesse az OpenTelemetry elnevezési irányelveit, amelyek kisbetűs pontozott hierarchianeveket 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ímkenevek:

    • customer.country
    • store.payment_method
    • store.purchase_result
  • Óvakodjon attól, hogy a gyakorlatban nagyon nagy vagy kötetlen címkeértékek vannak 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. A tárolás shortCounter<short> például csak címkekombinációnként 2 bájtot foglal el, míg az a doubleCounter<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. A gyakran hívandó és Record gyakran hívandó Add nagy teljesítményű kódok esetében érdemes ugyanazt a címkeneveket használni az egyes hívásokhoz.

  • A .NET API úgy van optimalizálva, hogy kiosztásmentes Add legyen, és Record egyenként három vagy kevesebb címkével rendelkező hívásokat hívjon meg. A nagyobb számú címkét tartalmazó foglalások elkerülése érdekében használja TagLista következőt: . Általánosságban elmondható, hogy a hívások teljesítményterhelése nő, mivel több címkét használnak.

Megjegyzés:

Az OpenTelemetria attribútumként hivatkozik a címkékre. Ezek két különböző nevet jelentenek ugyanahhoz a funkcióhoz.

Egyéni metrikák tesztelése

A segítségével hozzáadott MetricCollector<T>egyéni metrikákat tesztelheti. 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);
    }
}