新式 .NET 應用程式可以使用 API 擷取計量 System.Diagnostics.Metrics 。 這些計量通常以稱為 標籤 的索引鍵/值組形式包含其他內容(有時稱為遙測系統中 的維度 )。 本文說明如何使用編譯時來源生成器來定義強類型計量標記(TagNames)以及計量記錄類型和方法。 藉由使用強型別標籤,您可以消除樣板程式碼,並確保相關的度量在編譯時具備安全性,共用同一組標籤名稱。 此方法的主要優點是改善開發人員生產力和類型安全性。
備註
在計量的內容中,標籤有時也稱為「維度」。本文使用 「tag」 來清楚明瞭與 .NET 計量術語的一致性。
開始
若要開始使用,請安裝 📦 Microsoft.Extensions.Telemetry.Abstractions NuGet 套件:
如需詳細資訊,請參閱 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")。
這三個計量定義 CreateLatency、 CreateTotalCount和 CreateTotalFailures 都當做其標記物件類型使用 MetricTags 。 這表示產生的計量類型 (Latency、 TotalCount和 TotalFailures) 都會在記錄數據時預期實例 MetricTags 。
每個計量都會有一組相同的標記名稱:Dim1DimensionName、、 Operation、 Dim2、 Dim3和 DimensionNameOfParentOperation。
下列程式代碼示範如何在 類別中建立和使用這些計量:
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 記錄經過的時間,計數器(TotalCount 和 TotalFailures)分別記錄事件的次數。 由於所有計量都共用相同的標記物件類型,因此每個度量都會有標記 (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或數值類型),請在將值指派給標記物件之前,先將值轉換成字串。
遵守這些需求可確保來源產生器可以成功產生計量類型和方法。
另請參閱
- .NET 中來源產生的計量
- 在 .NET 中建立計量 (檢測教學課程)
- 在 .NET 中收集計量(使用 MeterListener 和匯出器)
- .NET 中的記錄來源產生 (適用於套用至記錄的類似來源產生方法)