Sdílet prostřednictvím


Metriky generované zdrojem se silně typovanými značkami

Moderní aplikace .NET můžou zaznamenávat metriky pomocí rozhraní System.Diagnostics.Metrics API. Tyto metriky často zahrnují další kontext ve formě párů klíč-hodnota označovaných jako značky (někdy označované jako dimenze v systémech telemetrie). Tento článek ukazuje, jak pomocí generátoru zdrojového kódu v době kompilace definovat značky metrik silného typu (TagNames) a typy a metody pro zaznamenávání metrik. Použitím značek se silnými typy eliminujete opakující se šablonovitý kód a zajistíte, aby související metriky sdílely stejnou sadu názvů značek s bezpečností při kompilaci. Hlavní výhodou tohoto přístupu je vyšší produktivita vývojářů a bezpečnost typů.

Poznámka

V kontextu metrik se značka někdy označuje také jako "dimenze". Tento článek používá značky pro přehlednost a konzistenci s terminologií metrik .NET.

Začínáme

Začněte instalací 📦 balíčku NuGet Microsoft.Extensions.Telemetry.Abstractions :

dotnet add package Microsoft.Extensions.Telemetry.Abstractions

Další informace viz dotnet add package nebo Manage package dependencies in .NET applications.

Výchozí názvy značek a jejich přizpůsobení

Ve výchozím nastavení zdrojový generátor odvozuje názvy značek metrik z polí a názvů vlastností vaší třídy značek. Jinými slovy, každé veřejné pole nebo vlastnost v objektu značky s pevným typem se standardně stává názvem značky. Můžete to přepsat pomocí TagNameAttribute v poli nebo vlastnosti určením vlastního názvu značky. V následujících příkladech uvidíte oba přístupy v akci.

Příklad 1: Základní metrika s jednou značkou

Následující příklad ukazuje jednoduchou počítací metriku s jednou značkou. V tomto scénáři chceme spočítat počet zpracovaných požadavků a kategorizovat je podle značky Region:

public struct RequestTags
{
    public string Region { get; set; }
}

public static partial class MyMetrics
{
    [Counter<int>(typeof(RequestTags))]
    public static partial RequestCount CreateRequestCount(Meter meter);
}

V předchozím kódu RequestTags je struktura značky silného typu s jedinou vlastností Region. Metoda CreateRequestCount je označena CounterAttribute<T>, kde T je int, což označuje, že generuje Counter nástroj, který umožňuje sledování int hodnot. Odkazy na typeof(RequestTags) atributy, což znamená, že čítač používá značky definované ve RequestTags, když zaznamenává metriky. Zdrojový generátor vytvoří silně typovanou instrumentační třídu (pojmenovanou RequestCount) s metodou Add, která přijímá celočíselnou hodnotu a objekt RequestTags.

Pokud chcete použít vygenerovanou metriku, vytvořte Meter a poznamenejte si měření, jak je znázorněno níže:

Meter meter = new("MyCompany.MyApp", "1.0");
RequestCount requestCountMetric = MyMetrics.CreateRequestCount(meter);

// Create a tag object with the relevant tag value
var tags = new RequestTags { Region = "NorthAmerica" };

// Record a metric value with the associated tag
requestCountMetric.Add(1, tags);

V tomto příkladu použití volání MyMetrics.CreateRequestCount(meter) vytvoří počítací nástroj (prostřednictvím Meter) a vrátí RequestCount metriku. Při volání requestCountMetric.Add(1, tags)zaznamenává systém metrik počet 1 spojený se značkou Region="NorthAmerica". Můžete znovu použít objekt RequestTags nebo vytvořit nové objekty pro počítání v různých oblastech, a název značky Region se bude konzistentně používat na každé měření.

Příklad 2: Metrika s vnořenými objekty značek

V případě složitějších scénářů můžete definovat třídy značek, které zahrnují více značek, vnořených objektů nebo dokonce zděděné vlastnosti. To umožňuje skupině souvisejících metrik efektivně sdílet společnou sadu značek. V dalším příkladu definujete sadu tříd značek a použijete je pro tři různé metriky:

using Microsoft.Extensions.Diagnostics.Metrics;

namespace MetricsGen;

public class MetricTags : MetricParentTags
{
    [TagName("Dim1DimensionName")]
    public string? Dim1;                      // custom tag name via attribute
    public Operations Operation { get; set; } // tag name defaults to "Operation"
    public MetricChildTags? ChildTagsObject { get; set; }
}

public enum Operations
{
    Unknown = 0,
    Operation1 = 1,
}

public class MetricParentTags
{
    [TagName("DimensionNameOfParentOperation")]
    public string? ParentOperationName { get; set; }  // custom tag name via attribute
    public MetricTagsStruct ChildTagsStruct { get; set; }
}

public class MetricChildTags
{
    public string? Dim2 { get; set; }  // tag name defaults to "Dim2"
}

public struct MetricTagsStruct
{
    public string Dim3 { get; set; }   // tag name defaults to "Dim3"
}

Předchozí kód definuje dědičnost metrik a obrazce objektů. Následující kód ukazuje použití těchto obrazců s generátorem, jak je znázorněno ve Metric třídě:

using System.Diagnostics.Metrics;
using Microsoft.Extensions.Diagnostics.Metrics;

public static partial class Metric
{
    [Histogram<long>(typeof(MetricTags))]
    public static partial Latency CreateLatency(Meter meter);

    [Counter<long>(typeof(MetricTags))]
    public static partial TotalCount CreateTotalCount(Meter meter);

    [Counter<int>(typeof(MetricTags))]
    public static partial TotalFailures CreateTotalFailures(Meter meter);
}

V tomto příkladu je MetricTags třída značky, která dědí z MetricParentTags a obsahuje také vnořený objekt značky (MetricChildTags) a vnořenou strukturu (MetricTagsStruct). Vlastnosti značky ukazují výchozí i přizpůsobené názvy značek:

  • Pole Dim1 v MetricTags má atribut [TagName("Dim1DimensionName")], takže název značky bude "Dim1DimensionName".
  • Vlastnost Operation nemá žádný atribut, takže název značky je standardně nastaven na "Operation".
  • V MetricParentTagsje vlastnost ParentOperationName přepsána názvem vlastní značky "DimensionNameOfParentOperation".
  • Vnořená třída MetricChildTags definuje vlastnost Dim2 (žádný atribut, název značky "Dim2").
  • Struktura MetricTagsStruct definuje pole Dim3 (název značky "Dim3").

Všechny tři definice metrik CreateLatency, CreateTotalCounta CreateTotalFailures jako typ objektu značky používají MetricTags. To znamená, že generované typy metrik (Latency, TotalCounta TotalFailures) budou při zaznamenávání dat očekávat MetricTags instanci. Každá z těchto metrik bude mít stejnou sadu názvů značek:Dim1DimensionName, Operation, Dim2, Dim3a DimensionNameOfParentOperation.

Následující kód ukazuje, jak vytvořit a použít tyto metriky ve třídě:

internal class MyClass
{
    private readonly Latency _latencyMetric;
    private readonly TotalCount _totalCountMetric;
    private readonly TotalFailures _totalFailuresMetric;

    public MyClass(Meter meter)
    {
        // Create metric instances using the source-generated factory methods
        _latencyMetric = Metric.CreateLatency(meter);
        _totalCountMetric = Metric.CreateTotalCount(meter);
        _totalFailuresMetric = Metric.CreateTotalFailures(meter);
    }

    public void DoWork()
    {
        var startingTimestamp = Stopwatch.GetTimestamp();
        bool requestSuccessful = true;
        // Perform some operation to measure
        var elapsedTime = Stopwatch.GetElapsedTime(startingTimestamp);

        // Create a tag object with values for all tags
        var tags = new MetricTags
        {
            Dim1 = "Dim1Value",
            Operation = Operations.Operation1,
            ParentOperationName = "ParentOpValue",
            ChildTagsObject = new MetricChildTags
            {
                Dim2 = "Dim2Value",
            },
            ChildTagsStruct = new MetricTagsStruct
            {
                Dim3 = "Dim3Value"
            }
        };

        // Record the metric values with the associated tags
        _latencyMetric.Record(elapsedTime.ElapsedMilliseconds, tags);
        _totalCountMetric.Add(1, tags);
        if (!requestSuccessful)
        {
            _totalFailuresMetric.Add(1, tags);
        }
    }
}

V předchozí MyClass.DoWork metodě se MetricTags objekt naplní hodnotami pro každou značku. Tento jediný tags objekt se pak při záznamu dat předá všem třem nástrojům. Metrika Latency (histogram) zaznamenává uplynulý čas a oba čítače (TotalCount i TotalFailures) zaznamenávají počty výskytů. Vzhledem k tomu, že všechny metriky sdílejí stejný typ objektu značky, jsou značky (Dim1DimensionName, Operation, Dim2, Dim3, DimensionNameOfParentOperation) přítomné na každém měření.

Určení jednotek

Počínaje rozhraním .NET 10.2 můžete volitelně zadat měrnou jednotku pro metriky pomocí parametru Unit . To pomáhá poskytnout kontext o tom, co metrika měří (například "sekundy", "bajty" a "požadavky"). Jednotka se předá základnímu objektu Meter při vytváření nástroje.

Následující kód ukazuje použití generátoru s primitivními typy se zadanými jednotkami:

public static partial class Metric
{
    [Histogram<long>(typeof(MetricTags), Unit = "ms")]
    public static partial Latency CreateLatency(Meter meter);

    [Counter<long>(typeof(MetricTags), Unit = "requests")]
    public static partial TotalCount CreateTotalCount(Meter meter);

    [Counter<int>(typeof(MetricTags), Unit = "failures")]
    public static partial TotalFailures CreateTotalFailures(Meter meter);
}

Důležité informace o výkonu

Použití značek se silnými typy prostřednictvím generování kódu nepřidává režijní náklady ve srovnání s přímým použitím metrik. Pokud potřebujete ještě více minimalizovat přidělení pro velmi vysokofrekvenční metriky, zvažte definování objektu značky jako struct (typ hodnoty) místo class. Použití struct pro objekt značky může zabránit přidělení haldy při zaznamenávání metrik, protože značky by byly předány hodnotou.

Požadavky na vygenerovanou metodu metriky

Při definování metod pro tvorbu metriku (částečné metody označené [Counter], [Histogram]atd.) má generátor kódu několik požadavků:

  • Každá metoda musí být public static partial (aby zdrojový generátor poskytl implementaci).
  • Návratový typ každé částečné metody musí být jedinečný (aby generátor mohl vytvořit jedinečně pojmenovaný typ pro metriku).
  • Název metody by neměl začínat podtržítkem (_) a názvy parametrů by neměly začínat podtržítkem.
  • Prvním parametrem musí být Meter (jedná se o instanci měřiče použitou k vytvoření podkladového nástroje).
  • Metody nemohou být obecné a nemohou mít obecné parametry.
  • Vlastnosti značky ve třídě značek mohou být pouze typu string nebo enum. U jiných typů (například bool nebo číselných typů) před přiřazením k objektu značky převeďte hodnotu na řetězec.

Dodržování těchto požadavků zajišťuje, že zdrojový generátor dokáže úspěšně vytvořit typy a metody metrik.

Viz také