Sdílet prostřednictvím


Vytváření metrik

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

Aplikace .NET je možné instrumentovat pomocí System.Diagnostics.Metrics rozhraní API ke sledování důležitých metrik. Některé metriky jsou součástí standardních knihoven .NET, ale můžete chtít přidat nové vlastní metriky, které jsou relevantní pro vaše aplikace a knihovny. V tomto kurzu přidáte nové metriky a pochopíte, jaké typy metrik jsou k dispozici.

Poznámka:

.NET má některá starší rozhraní API metrik, konkrétně EventCounters a System.Diagnostics.PerformanceCounter, které zde nejsou popsané. Další informace o těchto alternativách najdete v tématu Porovnání rozhraní API metrik.

Vytvořené vlastní metriky

Požadavky: Sada .NET Core 6 SDK nebo novější verze

Vytvořte novou konzolovou aplikaci, která odkazuje na balíček NuGet System.Diagnostics.DiagnosticSource verze 8 nebo vyšší. Aplikace, které cílí na .NET 8 nebo novější, zahrnují tento odkaz ve výchozím nastavení. Pak aktualizujte kód Program.cs tak, aby odpovídal:

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

Typ System.Diagnostics.Metrics.Meter je vstupním bodem pro knihovnu, která vytvoří pojmenovanou skupinu nástrojů. Nástroje zaznamenávají číselné hodnoty potřebné k výpočtu metrik. Zde jsme použili CreateCounter k vytvoření nástroje Counter s názvem "hatco.store.hats_sold". Během každé transakce předstírat kód volání Add k zaznamenání měření klobouků, které byly prodány, 4 v tomto případě. Nástroj "hatco.store.hats_sold" implicitně definuje některé metriky, které je možné vypočítat z těchto měření, například celkový počet prodaných klobouků nebo prodané klobouky za sekundu. Nakonec je to až nástroje pro shromažďování metrik, které určují, které metriky se mají vypočítat a jak tyto výpočty provádět, ale každý nástroj má některé výchozí konvence, které vyjadřují záměr vývojáře. U nástrojů Counter je konvence, že nástroje kolekce zobrazují celkový počet a/nebo rychlost, s jakou se počet zvyšuje.

Obecný parametr int zapnutý Counter<int> a CreateCounter<int>(...) definuje, že tento čítač musí být schopen ukládat hodnoty až Int32.MaxValuedo . Můžete použít libovolnou z bytehodnot , , longfloatdoubleshortintnebo decimal v závislosti na velikosti dat, která potřebujete uložit a zda jsou potřeba desetinné hodnoty.

Spusťte aplikaci a nechte ji spuštěnou. Na další metriky se podíváme.

> dotnet run
Press any key to exit

Osvědčené postupy

  • Pro kód, který není určen pro použití v kontejneru injektáže závislostí (DI), vytvořte měřič jednou a uložte ho do statické proměnné. Pro použití ve statických proměnných knihoven pracujících s DI se považují za anti-vzor a níže uvedený příklad DI ukazuje idiotický přístup. Každá knihovna nebo podkomponent knihovny (a často by měla) vytvořit vlastní Meter. Pokud očekáváte, že vývojáři aplikací budou moct snadno povolit a zakázat skupiny metrik samostatně, zvažte vytvoření nového měřiče a neopakujte stávající měřič.

  • Název předaný konstruktoru Meter by měl být jedinečný, aby ho odlišil od ostatních měřičů. Doporučujeme pokyny pro pojmenování OpenTelemetry, které používají tečkované hierarchické názvy. Názvy sestavení nebo názvy oborů názvů pro instrumentovaný kód jsou obvykle dobrou volbou. Pokud sestavení přidá instrumentaci pro kód v sekundě nezávislé sestavení, název by měl být založen na sestavení, které definuje měřič, nikoli sestavení, jehož kód je instrumentován.

  • .NET nevynucuje žádné schéma pojmenování pro nástroje, ale doporučujeme postupovat podle pokynů pro pojmenování OpenTelemetry, které používají malá písmena s tečkovanými hierarchickými názvy a podtržítkem ('_') jako oddělovač mezi více slovy ve stejném prvku. Ne všechny nástroje metriky zachovávají název měřiče jako součást konečného názvu metriky, takže je vhodné, aby byl název nástroje globálně jedinečný sám.

    Příklady názvů instrumentů:

    • contoso.ticket_queue.duration
    • contoso.reserved_tickets
    • contoso.purchased_tickets
  • Rozhraní API pro vytváření nástrojů a měření záznamů jsou bezpečná pro přístup z více vláken. V knihovnách .NET většina metod instancí vyžaduje synchronizaci při vyvolání stejného objektu z více vláken, ale to v tomto případě není potřeba.

  • Rozhraní API instrumentace k zaznamenávání měření (Add v tomto příkladu) obvykle běží v <10 ns, když se neshromažďují žádná data, nebo desítky až stovky nanosekund při shromažďování měření knihovnou nebo nástrojem pro vysoce výkonné kolekce. To umožňuje, aby se tato rozhraní API ve většiněpřípadůchm zařízením používala ve většině případů, ale dbejte na kód, který je extrémně citlivý na výkon.

Zobrazení nové metriky

Existuje mnoho možností, jak ukládat a zobrazovat metriky. Tento kurz používá nástroj dotnet-counters , který je užitečný pro ad hoc analýzu. Můžete se také podívat na kurz shromažďování metrik pro další alternativy. Pokud nástroj dotnet-counters ještě není nainstalovaný, nainstalujte ho pomocí sady SDK:

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

I když je ukázková aplikace stále spuštěná, pomocí dotnet-counters monitorujte nový čítač:

> 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

Podle očekávání můžete vidět, že HatCo store neustále prodává 4 klobouky za každou sekundu.

Získání měřiče prostřednictvím injektáže závislostí

V předchozím příkladu byl měřič získán jeho vytvořením new a přiřazením ke statickému poli. Použití statických prostředků tímto způsobem není dobrý přístup při použití injektáže závislostí (DI). V kódu, který používá DI, například ASP.NET Core nebo aplikace s obecným hostitelem, vytvořte objekt měřiče pomocí IMeterFactory. Počínaje rozhraním .NET 8 se hostitelé automaticky zaregistrují IMeterFactory v kontejneru služby nebo můžete typ ručně zaregistrovat IServiceCollection voláním AddMetrics. Továrna měřiče integruje metriky s DI a udržuje měřiče v různých kolekcích služeb izolované od sebe, i když používají stejný název. To je zvlášť užitečné pro testování, aby více testů spuštěných paralelně sledovalo měření vytvořená pouze v rámci stejného testovacího případu.

Chcete-li získat měřič v typu určeném pro DI, přidejte IMeterFactory do konstruktoru parametr a potom zavolejte Create. Tento příklad ukazuje použití IMeterFactory v aplikaci ASP.NET Core.

Definujte typ pro uložení nástrojů:

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

Zaregistrujte typ v kontejneru DI v Program.cssouboru .

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<HatCoMetrics>();

Tam, kde je to potřeba, zadejte typ metriky a poznamenejte si hodnoty. Vzhledem k tomu, že je typ metrik zaregistrovaný v DI, je možné ho použít s řadiči MVC, minimálními rozhraními API nebo jakýmkoli jiným typem, který je vytvořen pomocí DI:

app.MapPost("/complete-sale", ([FromBody] SaleModel model, HatCoMetrics metrics) =>
{
    // ... business logic such as saving the sale to a database ...

    metrics.HatsSold(model.QuantitySold);
});

Osvědčené postupy

  • System.Diagnostics.Metrics.Meter implementuje IDisposable, ale IMeterFactory automaticky spravuje životnost všech Meter objektů, které vytvoří, a dispoziuje je při odstranění kontejneru DI. Není nutné přidávat další kód pro vyvolání Dispose() v souboru Metera nebude mít žádný vliv.

Typy nástrojů

Zatím jsme ukázali Counter<T> pouze nástroj, ale k dispozici je více typů nástrojů. Nástroje se liší dvěma způsoby:

  • Výchozí výpočty metrik – Nástroje, které shromažďují a analyzují měření instrumentů, počítají různé výchozí metriky v závislosti na daném nástroji.
  • Úložiště agregovaných dat – Nejužitečnější metriky vyžadují agregaci dat z mnoha měření. Jednou z možností je, že volající poskytuje individuální měření v libovolných časech a nástroj kolekce spravuje agregaci. Volající může také spravovat agregované měření a poskytovat je na vyžádání v zpětném volání.

Typy nástrojů, které jsou aktuálně k dispozici:

  • Čítač (CreateCounter) – Tento nástroj sleduje hodnotu, která se v průběhu času zvyšuje, a volající hlásí přírůstky pomocí Add. Většina nástrojů vypočítá celkový součet a míru změny v součtu. U nástrojů, které ukazují jen jednu věc, se doporučuje četnost změn. Předpokládejme například, že volající volá Add() jednou za sekundu s následnými hodnotami 1, 2, 4, 5, 4, 3. Pokud se nástroj kolekce aktualizuje každých tři sekundy, je celkový počet po třech sekundách 1+2+4=7 a celkový počet po šesti sekundách je 1+2+4+5+4+3=19. Míra změny je (current_total - previous_total), takže nástroj po třech sekundách hlásí 7-0=7 a po šesti sekundách hlásí 19-7=12.

  • UpDownCounter (CreateUpDownCounter) – tento nástroj sleduje hodnotu, která se může v průběhu času zvýšit nebo snížit. Volající hlásí přírůstky a dekrementy pomocí Add. Předpokládejme například, že volající volá Add() jednou za sekundu s po sobě jdoucími hodnotami 1, 5, -2, 3, -1, -3. Pokud se nástroj kolekce aktualizuje každé tři sekundy, je celkový součet po třech sekundách 1+5-2=4 a celkový počet po šesti sekundách je 1+5-2+3-1-3=3.

  • ObservableCounter (CreateObservableCounter) – Tento nástroj je podobný čítači s tím rozdílem, že volající teď zodpovídá za udržování agregovaného součtu. Volající poskytuje delegáta zpětného volání při vytvoření ObservableCounter a zpětné volání se vyvolá pokaždé, když nástroje potřebují sledovat aktuální součet. Pokud například nástroj kolekce aktualizuje každé tři sekundy, funkce zpětného volání se vyvolá také každé tři sekundy. Většina nástrojů bude mít celkovou i míru změny celkového dostupného počtu nástrojů. Pokud se dá zobrazit jenom jedna, doporučuje se míra změn. Pokud zpětné volání vrátí hodnotu 0 při počátečním volání, 7, když se znovu zavolá po třech sekundách, a 19 při volání po šesti sekundách, nástroj ohlásí tyto hodnoty beze změny jako součty. U míry změn nástroj zobrazí po třech sekundách 7-0=7 a po šesti sekundách 19-7=12.

  • ObservableUpDownCounter (CreateObservableUpDownCounter) – tento nástroj je podobný upDownCounter s tím rozdílem, že volající je teď zodpovědný za udržování agregovaného součtu. Volající poskytuje delegát zpětného volání při vytvoření ObservableUpDownCounter a zpětné volání se vyvolá pokaždé, když nástroje potřebují sledovat aktuální součet. Pokud například nástroj kolekce aktualizuje každé tři sekundy, funkce zpětného volání se vyvolá také každé tři sekundy. Jakákoli hodnota vrácená zpětným voláním se zobrazí v nástroji kolekce beze změny jako součet.

  • ObservableGauge (CreateObservableGauge) – tento nástroj umožňuje volajícímu poskytnout zpětné volání, ve kterém se naměřená hodnota předává přímo jako metrika. Pokaždé, když se nástroj kolekce aktualizuje, vyvolá se zpětné volání a v nástroji se zobrazí jakákoli hodnota vrácená zpětným voláním.

  • Histogram (CreateHistogram) – tento nástroj sleduje rozdělení měření. Neexistuje jediný kanonický způsob, jak popsat sadu měření, ale doporučuje se použít histogramy nebo vypočítané percentily. Předpokládejme například, že volající vyvolal Record záznam těchto měření během intervalu aktualizace nástroje kolekce: 1,5,2,3,10,9,7,4,6,8. Nástroj kolekce může hlásit, že 50., 90. a 95. percentil těchto měření je 5, 9 a 9.

Osvědčené postupy při výběru typu nástroje

  • Pro počítání věcí nebo jakékoli jiné hodnoty, které se pouze v průběhu času zvyšují, použijte Counter nebo ObservableCounter. V závislosti na tom, které je snazší přidat do existujícího kódu, si můžete vybrat mezi čítačem a ObservableCounter: voláním rozhraní API pro každou operaci přírůstku nebo zpětným voláním, které přečte aktuální součet z proměnné, kterou kód udržuje. V extrémně horkých cestách kódu, kde je výkon důležitý a použití Add by vytvořilo více než milion volání za sekundu za vlákno, může použití ObservableCounter nabídnout více příležitostí pro optimalizaci.

  • Pro časování je obvykle upřednostňovaný histogram. Často je vhodné porozumět chvostům těchto rozdělení (90. , 95. percentil, 99. percentil) místo průměrů nebo součtů.

  • Jiné běžné případy, jako jsou míry dosažení mezipaměti nebo velikosti mezipamětí, front a souborů, jsou obvykle vhodné pro UpDownCounter nebo ObservableUpDownCounter. Vyberte si mezi nimi v závislosti na tom, který kód se snadněji přidá do existujícího kódu: buď volání rozhraní API pro každou operaci přírůstku a dekrementace, nebo zpětné volání, které přečte aktuální hodnotu z proměnné, kterou kód udržuje.

Poznámka:

Pokud používáte starší verzi .NET nebo balíček NuGet DiagnosticSource, který nepodporuje UpDownCounter a ObservableUpDownCounter (před verzí 7), ObservableGauge je často dobrou náhradou.

Příklad různých typů nástrojů

Zastavte dříve spuštěný ukázkový proces a nahraďte ukázkový kód Program.cs následujícím kódem:

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

Spusťte nový proces a pomocí dotnet-counters jako předtím v druhém prostředí zobrazte metriky:

> 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

V tomto příkladu se používají nějaká náhodně vygenerovaná čísla, takže se hodnoty budou trochu lišit. Vidíte, že hatco.store.hats_sold (čítač) i hatco.store.coats_sold (ObservableCounter) se obě zobrazují jako sazba. ObservableGauge, hatco.store.orders_pendingse zobrazí jako absolutní hodnota. Dotnet-counters vykresluje nástroje Histogramu jako tři percentilové statistiky (50. 95. a 99. a 99.), ale jiné nástroje můžou shrnout rozdělení odlišně nebo nabídnout více možností konfigurace.

Osvědčené postupy

  • Histogramy obvykle ukládají mnohem více dat do paměti než jiné typy metrik. Přesné využití paměti je však určeno použitým nástrojem kolekce. Pokud definujete velké množství metrik Histogramu (>100), možná budete muset uživatelům poskytnout pokyny, aby je nepovolili ve stejnou dobu, nebo nakonfigurovat nástroje pro úsporu paměti snížením přesnosti. Některé nástroje kolekce můžou mít pevné limity počtu souběžných histogramů, které budou monitorovat, aby se zabránilo nadměrnému využití paměti.

  • Zpětná volání pro všechny pozorovatelné nástroje jsou vyvolána v posloupnosti, takže jakákoli zpětná volání, která trvá dlouhou dobu, může zpozdit nebo zabránit shromažďování všech metrik. Upřednostnění rychlého čtení hodnoty uložené v mezipaměti, vrácení žádných měření nebo vyvolání výjimky nad prováděním potenciálně dlouhotrvající nebo blokující operace.

  • Zpětná volání ObservableCounter, ObservableUpDownCounter a ObservableGauge probíhají ve vlákně, které obvykle není synchronizované s kódem, který aktualizuje hodnoty. Je vaší zodpovědností buď synchronizovat přístup k paměti, nebo přijmout nekonzistentní hodnoty, které můžou mít za následek použití nesynchronizovaného přístupu. Běžnými přístupy k synchronizaci přístupu jsou použití zámku nebo volání Volatile.Read a Volatile.Write.

  • CreateObservableCounter Funkce CreateObservableGauge vrací objekt instrumentu, ale ve většině případů ho nemusíte ukládat do proměnné, protože není nutná žádná další interakce s objektem. Přiřazení statické proměnné jako u ostatních nástrojů je legální, ale náchylné k chybám, protože statická inicializace jazyka C# je opožděná a proměnná se obvykle nikdy neodkazuje. Tady je příklad problému:

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

Popisy a jednotky

Nástroje mohou určovat volitelné popisy a jednotky. Tyto hodnoty jsou neprůkazné pro všechny výpočty metrik, ale je možné je zobrazit v uživatelském rozhraní nástroje kolekce, aby technici pochopili, jak interpretovat data. Zastavte ukázkový proces, který jste začali dříve, a nahraďte ukázkový kód Program.cs následujícím kódem:

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

Spusťte nový proces a pomocí dotnet-counters jako předtím v druhém prostředí zobrazte metriky:

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

[HatCo.Store]
    hatco.store.hats_sold ({hats} / 1 sec)                                40

dotnet-counters v současné době nepoužívá text popisu v uživatelském rozhraní, ale zobrazuje jednotku, když je k dispozici. V tomto případě se zobrazí, že {hats} nahradil obecný termín "Počet", který je viditelný v předchozích popisech.

Osvědčené postupy

  • Rozhraní .NET API umožňují použití libovolného řetězce jako jednotky, ale doporučujeme použít UCUM, což je mezinárodní standard pro názvy jednotek. Složené závorky kolem {hats} jsou součástí standardu UCUM, který označuje, že se jedná o popisnou poznámku, nikoli název jednotky se standardizovaným významem, jako jsou sekundy nebo bajty.

  • Jednotka zadaná v konstruktoru by měla popisovat jednotky vhodné pro jednotlivá měření. To se někdy liší od jednotek konečné metriky. V tomto příkladu je každé měření řadou klobouků, takže {hats} je vhodná jednotka pro předání konstruktoru. Nástroj kolekce vypočítal míru a odvozoval se od sebe, že příslušná jednotka pro počítanou metriku je {hats}/s.

  • Při zaznamenávání měření času upřednostňujete jednotky sekund zaznamenané jako plovoucí desetina nebo dvojitou hodnotu.

Multidimenzionální metriky

Měření lze také přidružit ke párům klíč-hodnota označovaným jako značky, které umožňují kategorizaci dat pro analýzu. Například HatCo může chtít zaznamenat nejen počet prodaných klobouků, ale také velikost a barvu, které byly. Při pozdější analýze dat mohou technici HatCo rozdělit součty podle velikosti, barvy nebo jakékoli kombinace obou.

Značky čítačů a histogramů je možné zadat v přetíženích Add a Record přebírají jeden nebo více KeyValuePair argumentů. Příklad:

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

Nahraďte kód Program.cs aplikace a čítačů dotnet-counter a znovu ho spusťte jako předtím:

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

Čítače Dotnet teď zobrazují základní kategorizaci:

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

Pro ObservableCounter a ObservableGauge lze označení měření poskytnout v zpětném volání předané konstruktoru:

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

Při spuštění s čítači dotnet-counters jako předtím je výsledek:

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

Osvědčené postupy

  • I když rozhraní API umožňuje použití libovolného objektu jako hodnoty značky, číselné typy a řetězce se očekávají nástroji kolekce. Jiné typy mohou nebo nemusí být podporovány daným nástrojem kolekce.

  • Názvy značek doporučujeme používat podle pokynů pro pojmenování OpenTelemetry, které používají malá písmena s tečkovanými názvy se znaky _, abyste oddělily více slov ve stejném prvku. Pokud se názvy značek opakovaně používají v různých metrikách nebo jiných záznamech telemetrie, měly by mít stejný význam a sadu právních hodnot všude, kde se používají.

    Příklady názvů značek:

    • customer.country
    • store.payment_method
    • store.purchase_result
  • Dávejte pozor na to, aby se v praxi zaznamenávaly velmi velké nebo nevázané kombinace hodnot značek. I když implementace rozhraní .NET API dokáže zpracovat, nástroje kolekce pravděpodobně přidělují úložiště pro data metrik přidružených ke každé kombinaci značek, což by mohlo být velmi velké. Je například v pořádku, pokud má HatCo 10 různých barev klobouků a 25 velikostí klobouků až pro 10*25=250 prodejních součtů, které se mají sledovat. Pokud však HatCo přidal třetí značku, která je ID zákazníka pro prodej a prodává 100 milionů zákazníků po celém světě, teď je pravděpodobné, že se zaznamenávají miliardy různých kombinací značek. Většinanástrojůchm nástrojům metrik buď zahodí data, aby zůstala v mezích technických limitů, nebo může existovat Implementace každého nástroje kolekce určí své limity, ale pravděpodobně méně než 1 000 kombinací pro jeden nástroj je bezpečné. Cokoli nad 1000 kombinací bude vyžadovat, aby nástroj kolekce použil filtrování nebo byl navržen tak, aby fungoval ve velkém měřítku. Implementace histogramů používají mnohem více paměti než jiné metriky, takže bezpečné limity můžou být 10 až 100krát nižší. Pokud očekáváte velký počet jedinečných kombinací značek, pak protokoly, transakční databáze nebo systémy pro zpracování velkých objemů dat mohou být vhodnější řešení pro provoz v potřebném měřítku.

  • U nástrojů, které budou mít velmi velký počet kombinací značek, preferujte použití menšího typu úložiště, aby se snížila režie na paměť. Například uložení short pouze Counter<short> 2 bajtů na kombinaci značek, zatímco double hodnota pro Counter<double> obsazení 8 bajtů na kombinaci značek.

  • Nástroje kolekce se doporučuje optimalizovat pro kód, který určuje stejnou sadu názvů značek ve stejném pořadí pro každé volání záznamu měření na stejném nástroji. U vysoce výkonného kódu, který je potřeba volat Add a Record často, raději pro každé volání použijte stejnou sekvenci názvů značek.

  • Rozhraní .NET API je optimalizované tak, aby bylo přidělování zdarma a Add Record volání se třemi nebo méně značkami zadanými jednotlivě. Pokud se chcete vyhnout přidělování s větším počtem značek, použijte TagList. Obecně platí, že režie na výkon těchto volání se zvyšuje, protože se používají více značek.

Poznámka:

OpenTelemetry označuje značky jako atributy. Jedná se o dva různé názvy pro stejnou funkci.

Testování vlastních metrik

Je možné otestovat všechny vlastní metriky, které přidáte pomocí MetricCollector<T>. Tento typ usnadňuje zaznamenání měření z konkrétních nástrojů a uplatnění hodnot byly správné.

Testování pomocí injektáže závislostí

Následující kód ukazuje příklad testovacího případu pro komponenty kódu, které používají injektáž závislostí a IMeterFactory.

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

Každý objekt MetricCollector zaznamenává všechna měření pro jeden instrument. Pokud potřebujete ověřit měření z více nástrojů, vytvořte pro každou z nich jeden MetricCollector.

Testování bez injektáže závislostí

Je také možné otestovat kód, který používá sdílený globální objekt měřiče ve statickém poli, ale ujistěte se, že tyto testy nejsou nakonfigurované tak, aby se spouštěly paralelně. Vzhledem k tomu, že se sdílí objekt Měřič, bude MetricCollector v jednom testu sledovat měření vytvořená z jiných testů spuštěných paralelně.

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