共用方式為


由來源產生,具有強型別標籤的指標

新式 .NET 應用程式可以使用 API 擷取計量 System.Diagnostics.Metrics 。 這些計量通常以稱為 標籤 的索引鍵/值組形式包含其他內容(有時稱為遙測系統中 的維度 )。 本文說明如何使用編譯時來源生成器來定義強類型計量標記(TagNames)以及計量記錄類型和方法。 藉由使用強型別標籤,您可以消除樣板程式碼,並確保相關的度量在編譯時具備安全性,共用同一組標籤名稱。 此方法的主要優點是改善開發人員生產力和類型安全性。

備註

在計量的內容中,標籤有時也稱為「維度」。本文使用 「tag」 來清楚明瞭與 .NET 計量術語的一致性。

開始

若要開始使用,請安裝 📦 Microsoft.Extensions.Telemetry.Abstractions NuGet 套件:

dotnet add package Microsoft.Extensions.Telemetry.Abstractions

如需詳細資訊,請參閱 dotnet 新增套件在 .NET 應用程式中管理套件相依性

標籤名稱預設值和自訂

根據預設,來源產生器會從標記類別的欄位和屬性名稱衍生計量標記名稱。 換句話說,強類型標籤物件中的每個公用欄位或屬性預設都會成為標籤名稱。 您可以在欄位或屬性上使用 TagNameAttribute 來指定自訂標籤名稱,從而覆寫此設定。 在下列範例中,您會看到這兩種方法的運作方式。

範例 1:具有單一標記的基本計量

下列範例示範一個具有一個標籤的簡單計數器指標。 在此案例中,我們想要計算已處理的要求數目,並將其分類為 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);
}

在上述程序代碼中, RequestTags 是具有單一屬性 Region的強型別標記結構。 方法CreateRequestCount會以CounterAttribute<T>T標示為int,表示其會產生一個Counter,用來追蹤int值。 屬性會參考 typeof(RequestTags),這表示計數器會在記錄計量時使用 中所 RequestTags 定義的標記。 來源產生器會產生一個強類型的工具類別(名稱為RequestCount),其方法 Add 接受整數值和 RequestTags 物件。

若要使用生成的度量,請建立Meter 並記錄測量,如下所示:

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

在此使用範例中,呼叫 MyMetrics.CreateRequestCount(meter) 會建立計數器檢測(透過 Meter),並傳 RequestCount 回計量物件。 當您呼叫 requestCountMetric.Add(1, tags) 時,計量系統會記錄與標記 Region="NorthAmerica" 相關聯的次數為 1。 您可以重複使用 RequestTags 物件或建立新的物件來記錄不同區域的計數,而且標記名稱 Region 會一致地套用至每個度量。

範例 2:具有嵌套標記物件的度量

針對更複雜的案例,您可以定義標籤類別,這些類別可包含多個標籤、巢狀物件,甚至繼承的屬性。 這可讓一組相關的計量有效地共用一組常見的標記。 在下一個範例中,您會定義一組標記類別,並將其用於三個不同的計量:

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"
}

上述程式代碼會定義計量繼承和物件圖形。 下列程式碼示範如何在Metric類別中搭配產生器使用這些圖形:

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

在此範例中, MetricTags 是繼承自 MetricParentTags 的標記類別,也包含巢狀標記物件 (MetricChildTags) 和巢狀結構 (MetricTagsStruct)。 標記屬性會示範預設和自訂的標籤名稱:

  • 中的Dim1MetricTags欄位具有 [TagName("Dim1DimensionName")] 屬性,因此其標籤名稱會是 "Dim1DimensionName"
  • 屬性 Operation 沒有屬性,因此其標籤名稱預設為 "Operation"
  • MetricParentTags中,ParentOperationName屬性會以自定義標籤名稱"DimensionNameOfParentOperation"覆寫。
  • 嵌套的 MetricChildTags 類別會定義一個名為 Dim2"Dim2" 屬性(無屬性)。
  • 結構 MetricTagsStruct 定義了一個 Dim3 欄位(標記名稱為 "Dim3")。

這三個計量定義 CreateLatencyCreateTotalCountCreateTotalFailures 都當做其標記物件類型使用 MetricTags 。 這表示產生的計量類型 (LatencyTotalCountTotalFailures) 都會在記錄數據時預期實例 MetricTags每個計量都會有一組相同的標記名稱:Dim1DimensionName、、 OperationDim2Dim3DimensionNameOfParentOperation

下列程式代碼示範如何在 類別中建立和使用這些計量:

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

在上述 MyClass.DoWork 方法中, MetricTags 物件會填入每個標記的值。 然後,當記錄數據時,這個單一物件會傳遞至這三個 tags 儀器。 指標(直方圖)Latency 記錄經過的時間,計數器(TotalCountTotalFailures)分別記錄事件的次數。 由於所有計量都共用相同的標記物件類型,因此每個度量都會有標記 (Dim1DimensionName、 、 OperationDim2Dim3、 、 DimensionNameOfParentOperation) 。

單位的指定

從 .NET 10.2 開始,你可以選擇性地用參數 Unit 來指定指標的單位。 這有助於提供指標所衡量的範圍(例如「秒」、「位元組」和「請求」)的背景。 在建立儀器時,該單元會傳給底層 Meter

以下程式碼示範如何在指定單位的原始型態中使用生成元:

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

效能考量

使用來源代碼產生的強型別標籤,相較於直接使用計量,不會增加額外負擔。 如果您需要進一步減少高頻度計量的資源分配,請考慮將標記物件定義為 struct(實值型別),而不是 class。 使用 struct 作為標記物件可以避免在記錄度量時進行堆記憶體配置,因為標記會以值傳遞方式傳遞。

生成的度量方法要求

定義度量工廠方法時(以 [Counter][Histogram] 等裝飾的部分方法),源代碼生成器會施加一些要求:

  • 每個方法都必須public static partial(這樣來源生成器才能提供實作)。
  • 每個部分方法的傳回型別必須是唯一的(讓產生器可以針對計量建立唯一的具名型別)。
  • 方法名稱不應該以底線 (_) 開頭,而參數名稱不應該以底線開頭。
  • 第一個參數必須是Meter(這是用來建立基礎儀器的計量實例)。
  • 方法不可以是泛型,而且不能有泛型參數。
  • 標記類別中的標籤屬性只能是string類型或enum類型。 若為其他類型(例如 bool 或數值類型),請在將值指派給標記物件之前,先將值轉換成字串。

遵守這些需求可確保來源產生器可以成功產生計量類型和方法。

另請參閱