.NET 和 .NET Core 中的自定义指标集合

Azure Monitor Application Insights.NET 和 .NET Core SDK 有两种不同的收集自定义指标的方法:TrackMetric()GetMetric()。 这两种方法的主要区别在于本地聚合。 TrackMetric() 方法缺少预聚合。 GetMetric() 方法具有预聚合。 建议使用聚合,因此,TrackMetric() 不再是收集自定义指标的首选方法。 本文介绍如何使用 GetMetric() 方法及其工作原理背后的一些基本原理。

注意

以下文档依赖于 Application Insights 经典 API。 Application Insights 的长期计划是使用 OpenTelemetry 收集数据。 有关详细信息,请参阅为 .NET、Node.js、Python 和 Java 应用程序启用 Azure Monitor OpenTelemetry

预聚合 API 与非预聚合 API

TrackMetric() 方法发送表示指标的原始遥测。 为每个值发送单个遥测项效率低。 TrackMetric() 方法在性能方面的效率也较低,因为每个 TrackMetric(item) 都要经过遥测初始化表达式和处理器的完整 SDK 管道。

TrackMetric() 不同,GetMetric() 为你处理本地预聚合,然后仅以一分钟的固定间隔提交聚合汇总指标。 如果你需要在秒级甚至毫秒级密切监视某些自定义指标,则可以这样做,同时只需要承担每分钟监视一次的存储和网络流量成本。 这样做也极大降低了出现限制的风险,因为需要为聚合指标发送的遥测项总数大大减少。

在 Application Insights 中,通过 TrackMetric()GetMetric() 收集的自定义指标不受采样限制。 对重要指标进行采样可能会导致以下情况:围绕这些指标构建的警报可能变得不可靠。 通过从不采样自定义指标,通常可以确信,一旦超过警报阈值,便会触发警报。 由于没有对自定义指标进行采样,因此存在一些潜在的问题。

如果需要每秒钟跟踪一个指标的趋势,或以更细粒度的间隔进行跟踪,这可能会导致:

  • 数据存储成本增加。 向 Azure Monitor 发送的数据量会产生一定的成本。 发送的数据越多,监视的总体成本就越高。
  • 网络流量或性能开销增加。 在某些情况下,此类开销可能会产生货币成本和应用程序性能成本。
  • 引入限制风险。 当应用以短时间间隔发送速率很高的遥测时,Azure Monitor 会丢弃(“限制”)数据点。

限制是一个问题,因为它可能导致错过警报。 触发警报的情况可能在本地发生,然后由于发送的数据太多而在引入终结点被丢弃。 这就是对于 .NET 和 .NET Core,我们不建议使用 TrackMetric() 的原因,除非你已实现自己的本地聚合逻辑。 如果尝试跟踪事件在给定时间段内发生的每个实例,则可能会发现 TrackEvent() 更适合。 请记住,与自定义指标不同,自定义事件受采样限制。 即使不编写自己的本地预聚合,也可以使用 TrackMetric()。 但如果这样做,请注意陷阱。

总之,建议使用 GetMetric(),因为它执行预聚合、从所有 Track() 调用中累积值,并每分钟发送一次汇总/聚合。 通过发送更少的数据点,同时仍然收集所有相关信息,GetMetric() 方法可以显著降低成本和性能开销。

注意

只有 .NET 和 .NET Core SDK 具有 GetMetric() 方法。 如果使用的是 Java,请参阅使用 Micrometer 发送自定义指标。 对于 JavaScript 和 Node.js,仍可以使用 TrackMetric(),但请记住上一部分总结的注意事项。 对于 Python,可以使用 OpenCensus.stats 发送自定义指标,但指标的实现是不同的。

GetMetric 入门

在示例中,我们将使用基本的 .NET Core 3.1 辅助角色服务应用程序。 如果你想复制与这些示例一起使用的测试环境,请遵循监视辅助角色服务一文中的步骤 1-6。 这些步骤会将 Application Insights 添加到基本辅助角色服务项目模板。 此概念适用于任何可以使用 SDK 的通用应用程序,包括 Web 应用和控制台应用。

发送指标

worker.cs 文件的内容替换为以下内容:

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.ApplicationInsights;

namespace WorkerService3
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private TelemetryClient _telemetryClient;

        public Worker(ILogger<Worker> logger, TelemetryClient tc)
        {
            _logger = logger;
            _telemetryClient = tc;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {   // The following line demonstrates usages of GetMetric API.
            // Here "computersSold", a custom metric name, is being tracked with a value of 42 every second.
            while (!stoppingToken.IsCancellationRequested)
            {
                _telemetryClient.GetMetric("ComputersSold").TrackValue(42);

                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(1000, stoppingToken);
            }
        }
    }
}

运行示例代码时,你将看到 while 循环重复执行,且不会在 Visual Studio 输出窗口中发送遥测数据。 将围绕 60 秒的标记发送单个遥测项,测试中如下所示:

Application Insights Telemetry: {"name":"Microsoft.ApplicationInsights.Dev.00000000-0000-0000-0000-000000000000.Metric", "time":"2019-12-28T00:54:19.0000000Z",
"ikey":"00000000-0000-0000-0000-000000000000",
"tags":{"ai.application.ver":"1.0.0.0",
"ai.cloud.roleInstance":"Test-Computer-Name",
"ai.internal.sdkVersion":"m-agg2c:2.12.0-21496",
"ai.internal.nodeName":"Test-Computer-Name"},
"data":{"baseType":"MetricData",
"baseData":{"ver":2,"metrics":[{"name":"ComputersSold",
"kind":"Aggregation",
"value":1722,
"count":41,
"min":42,
"max":42,
"stdDev":0}],
"properties":{"_MS.AggregationIntervalMs":"42000",
"DeveloperMode":"true"}}}}

此单个遥测项代表了 41 个不同指标度量的聚合。 由于我们反复发送相同的值,因此标准偏差 (stDev) 为 0,具有相同的最大值 (max) 和最小值 (min)。 value 属性表示聚合的所有单个值的总和。

注意

GetMetric 方法不支持跟踪最后一个值(例如 gauge)或跟踪直方图/分布。

如果我们在“日志(分析)”体验中检查 Application Insights 资源,此单独的遥测项将如以下屏幕截图所示。

Screenshot that shows the Log Analytics query view.

注意

虽然原始遥测项在引入后不包含显式的总和属性/字段,但我们会为你创建一个。 在本例中,valuevalueSum 属性都表示相同的内容。

还可以在门户的指标部分访问自定义指标遥测,作为基于日志的指标和自定义指标。 以下屏幕截图是基于日志的指标的一个示例。

Screenshot that shows the Metrics explorer view.

用于高吞吐量使用情况的缓存指标引用

在某些情况下,可能会经常观察到指标值。 例如,每秒处理 500 个请求的高吞吐量服务可能希望为每个请求发出 20 个遥测指标。 该结果意味着每秒跟踪 10,000 个值。 在这种高吞吐量方案中,用户可能需要通过避免一些查找来帮助 SDK。

例如,上述示例对指标 ComputersSold 的句柄执行了查找操作,然后跟踪了观察到的值 42。 相反,用户应该缓存该句柄,以进行多次跟踪调用:

//...

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            // This is where the cache is stored to handle faster lookup
            Metric computersSold = _telemetryClient.GetMetric("ComputersSold");
            while (!stoppingToken.IsCancellationRequested)
            {

                computersSold.TrackValue(42);

                computersSold.TrackValue(142);

                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(50, stoppingToken);
            }
        }

除了缓存指标句柄之外,上述示例还将 Task.Delay 缩短到了 50 毫秒,这样循环将更频繁地执行, 从而导致了 772 次 TrackValue() 调用。

多维指标

上一部分的示例显示零维度指标。 指标也可以是多维的。 目前最多支持 10 个维度。

下面是如何创建一维指标的示例:

//...

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            // This is an example of a metric with a single dimension.
            // FormFactor is the name of the dimension.
            Metric computersSold= _telemetryClient.GetMetric("ComputersSold", "FormFactor");

            while (!stoppingToken.IsCancellationRequested)
            {
                // The number of arguments (dimension values)
                // must match the number of dimensions specified while GetMetric.
                // Laptop, Tablet, etc are values for the dimension "FormFactor"
                computersSold.TrackValue(42, "Laptop");
                computersSold.TrackValue(20, "Tablet");
                computersSold.TrackValue(126, "Desktop");


                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(50, stoppingToken);
            }
        }

运行示例代码至少 60 秒,这会将三个不同的遥测项发送到 Azure。 每个项都表示这三种外形规格之一的聚合。 与之前一样,你可以在“日志(分析)”视图中执行进一步检查。

Screenshot that shows the Log Analytics view of multidimensional metric.

在指标资源管理器中:

Screenshot that shows Custom metrics.

请注意,你无法按新的自定义维度拆分指标,也无法使用指标视图查看自定义维度。

Screenshot that shows splitting support.

默认情况下,Application Insights 资源中未启用指标资源管理器中的多维指标。

启用多维指标

要为 Application Insights 资源启用多维指标,请选择“使用情况和估计成本”>“自定义指标”>“启用自定义指标维度的警报”>“确定”。 有关详细信息,请参阅自定义指标维度和预聚合

进行更改并发送新的多维遥测后,可以选择“应用拆分”。

注意

只有在门户中启用此功能后,新发送的指标才会存储维度。

Screenshot that shows applying splitting.

查看每个 FormFactor 维度的指标聚合。

Screenshot that shows form factors.

当维度超过三个时使用 MetricIdentifier

目前支持 10 个维度。 超过三个维度需要使用 MetricIdentifier

// Add "using Microsoft.ApplicationInsights.Metrics;" to use MetricIdentifier
// MetricIdentifier id = new MetricIdentifier("[metricNamespace]","[metricId],"[dim1]","[dim2]","[dim3]","[dim4]","[dim5]");
MetricIdentifier id = new MetricIdentifier("CustomMetricNamespace","ComputerSold", "FormFactor", "GraphicsCard", "MemorySpeed", "BatteryCapacity", "StorageCapacity");
Metric computersSold  = _telemetryClient.GetMetric(id);
computersSold.TrackValue(110,"Laptop", "Nvidia", "DDR4", "39Wh", "1TB");

自定义指标配置

如果想要更改指标配置,则必须在初始化指标的位置进行更改。

特殊维度名称

指标不使用用于访问它们的 TelemetryClient 的遥测上下文。 在 MetricDimensionNames 类中使用可用作常量的特殊维度名称是解决此限制的最佳方法。

以下 Special Operation Request Size 指标发送的指标聚合不会将 Context.Operation.Name 设置为 Special OperationTrackMetric() 方法或任何其他 TrackXXX() 方法会将 OperationName 正确设置为 Special Operation

        //...
        TelemetryClient specialClient;
        private static int GetCurrentRequestSize()
        {
            // Do stuff
            return 1100;
        }
        int requestSize = GetCurrentRequestSize()

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                //...
                specialClient.Context.Operation.Name = "Special Operation";
                specialClient.GetMetric("Special Operation Request Size").TrackValue(requestSize);
                //...
            }
                   
        }

在这种情况下,使用 MetricDimensionNames 类中列出的特殊维度名称来指定 TelemetryContext 值。

例如,当下一条语句生成的指标聚合发送到 Application Insights 云终结点时,其 Context.Operation.Name 数据字段将设置为 Special Operation

_telemetryClient.GetMetric("Request Size", MetricDimensionNames.TelemetryContext.Operation.Name).TrackValue(requestSize, "Special Operation");

此特殊维度的值将复制到 TelemetryContext 中,并且不会用作“普通”维度。 如果还希望用于普通指标探索的操作维度,则需要针对此目的创建单独的维度:

_telemetryClient.GetMetric("Request Size", "Operation Name", MetricDimensionNames.TelemetryContext.Operation.Name).TrackValue(requestSize, "Special Operation", "Special Operation");

维度和时序上限

要防止遥测子系统意外耗尽资源,可以控制每个指标的最大数据系列数。 默认限制是每个指标的总数据系列不超过 1,000 个,每个维度的不同值不超过 100 个。

重要

对维度使用低基数值以避免限制。

在维度和时序上限的上下文中,我们使用 Metric.TrackValue(..) 来确保遵守限制。 如果已经达到限制,Metric.TrackValue(..) 将返回 False,并且不会跟踪该值。 否则,它将返回 True。 如果指标的数据源于用户输入,该行为将很有用。

MetricConfiguration 构造函数可以选择如何在相应的指标以及实现 IMetricSeriesConfiguration 的类对象中管理不同的系列,该类为指标的每个单独系列指定聚合行为:

var metConfig = new MetricConfiguration(seriesCountLimit: 100, valuesPerDimensionLimit:2,
                new MetricSeriesConfigurationForMeasurement(restrictToUInt32Values: false));

Metric computersSold = _telemetryClient.GetMetric("ComputersSold", "Dimension1", "Dimension2", metConfig);

// Start tracking.
computersSold.TrackValue(100, "Dim1Value1", "Dim2Value1");
computersSold.TrackValue(100, "Dim1Value1", "Dim2Value2");

// The following call gives 3rd unique value for dimension2, which is above the limit of 2.
computersSold.TrackValue(100, "Dim1Value1", "Dim2Value3");
// The above call does not track the metric, and returns false.
  • seriesCountLimit 是指标可以包含的最大数据时序数目。 达到此限制时,对 TrackValue() 的调用将返回 false,该调用通常会生成新序列。
  • valuesPerDimensionLimit 以类似的方式限制每个维度的非重复值数目。
  • restrictToUInt32Values 确定是否只跟踪非负整数值。

以下示例演示如何发送消息以了解是否超过上限:

if (! computersSold.TrackValue(100, "Dim1Value1", "Dim2Value3"))
{
// Add "using Microsoft.ApplicationInsights.DataContract;" to use SeverityLevel.Error
_telemetryClient.TrackTrace("Metric value not tracked as value of one of the dimension exceeded the cap. Revisit the dimensions to ensure they are within the limits",
SeverityLevel.Error);
}

后续步骤