ASP.NET Core 指标

指标是一段时间内报告的数字度量值。 使用它们监视应用的运行状况并生成警报。 例如,Web 服务可能会跟踪以下数量:

  • 每秒接收的请求数。
  • 响应所需的毫秒数。
  • 发送的响应含有错误。

定期将这些指标报告给监视系统。 设置仪表板以查看指标并创建警报,以通知人员出现问题。 如果 Web 服务打算在 400 毫秒内响应请求,并在 600 毫秒后开始响应,则监视系统可以通知操作人员应用响应速度比平时慢。

ASP.NET Core内置指标中介绍了所有工具及其属性的综合列表。

使用指标

使用指标涉及以下内容:

  • 检测:.NET 库中的代码采用度量值,并将这些度量值与指标名称关联起来。 .NET 和 ASP.NET Core 包括许多内置指标。
  • 收集和存储:由一个 .NET 应用来配置要从应用传输的命名指标以用于外部存储和分析。 某些工具可能通过使用配置文件或 UI 工具在应用外部执行配置。
  • 可视化:一种可以以人类可读的格式显示指标的工具。 例如 GrafanaPrometheus
  • 警报:一个当指标超过阈值时提供通知的工具。 例如,如果 Web 服务的平均响应时间超过 400 毫秒,则可以向操作人员发送警报。
  • 分析:一个可以分析一段时间内的指标的工具。 此工具通常是一个基于 Web 的仪表板,可以自定义以显示特定应用最重要的指标。

检测的代码可以记录数值度量值,但需要聚合、传输和存储度量值,才能创建用于监视的有用指标。 聚合、传输和存储数据的过程称为集合。 本教程展示了收集和显示指标的几个示例:

还可以将度量值与称为标记的键值对相关联,以便对数据进行分类进行分析。 有关详细信息,请参阅多维指标

创建初学者应用

使用以下命令创建新的 ASP.NET Core 应用:

dotnet new web -o WebMetric
cd WebMetric
dotnet add package OpenTelemetry.Exporter.Prometheus.AspNetCore --prerelease
dotnet add package OpenTelemetry.Extensions.Hosting

Program.cs 的内容替换为以下代码:

using OpenTelemetry.Metrics;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry()
    .WithMetrics(builder =>
    {
        builder.AddPrometheusExporter();

        builder.AddMeter("Microsoft.AspNetCore.Hosting",
                         "Microsoft.AspNetCore.Server.Kestrel");
        builder.AddView("http.server.request.duration",
            new ExplicitBucketHistogramConfiguration
            {
                Boundaries = new double[] { 0, 0.005, 0.01, 0.025, 0.05,
                       0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10 }
            });
    });
var app = builder.Build();

app.MapPrometheusScrapingEndpoint();

app.MapGet("/", () => "Hello OpenTelemetry! ticks:"
                     + DateTime.Now.Ticks.ToString()[^3..]);

app.Run();

使用 dotnet-counters 查看指标

dotnet-counters 是一种命令行工具,可按需查看 .NET 应用的实时指标。 它不需要设置,因此可用于临时调查或验证指标检测是否正常工作。 它与基于 System.Diagnostics.Metrics 的 API 和 EventCounters 配合运作。

如果未安装 dotnet-counters 工具,请运行以下命令:

dotnet tool update -g dotnet-counters

运行测试应用时,启动 dotnet-counters。 以下命令显示了一个 dotnet-counters 示例,它监视来自 Microsoft.AspNetCore.Hosting 计量的所有指标

dotnet-counters monitor -n WebMetric --counters Microsoft.AspNetCore.Hosting

显示了类似下面的输出:

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

[Microsoft.AspNetCore.Hosting]
    http-server-current-requests
        host=localhost,method=GET,port=5045,scheme=http                    0
    http-server-request-duration (s)
        host=localhost,method=GET,port=5045,protocol=HTTP/1.1,ro           0.001
        host=localhost,method=GET,port=5045,protocol=HTTP/1.1,ro           0.001
        host=localhost,method=GET,port=5045,protocol=HTTP/1.1,ro           0.001
        host=localhost,method=GET,port=5045,protocol=HTTP/1.1,ro           0
        host=localhost,method=GET,port=5045,protocol=HTTP/1.1,ro           0
        host=localhost,method=GET,port=5045,protocol=HTTP/1.1,ro           0

有关详细信息,请参阅 dotnet-counters

扩充 ASP.NET 核心请求指标

ASP.NET Core 具有许多内置指标。 http.server.request.duration指标:

  • 记录服务器上 HTTP 请求的持续时间。
  • 捕获标记中的请求信息,例如匹配的路由和响应状态代码。

http.server.request.duration 指标支持使用 IHttpMetricsTagsFeature 进行标签扩充。 在库或应用将自己的标记添加到指标时进行扩充。 如果应用想要向使用指标生成的仪表板或警报添加自定义分类,此功能非常有用。

using Microsoft.AspNetCore.Http.Features;

var builder = WebApplication.CreateBuilder();
var app = builder.Build();

app.Use(async (context, next) =>
{
    var tagsFeature = context.Features.Get<IHttpMetricsTagsFeature>();
    if (tagsFeature != null)
    {
        var source = context.Request.Query["utm_medium"].ToString() switch
        {
            "" => "none",
            "social" => "social",
            "email" => "email",
            "organic" => "organic",
            _ => "other"
        };
        tagsFeature.Tags.Add(new KeyValuePair<string, object?>("mkt_medium", source));
    }

    await next.Invoke();
});

app.MapGet("/", () => "Hello World!");

app.Run();

上面的示例:

  • 添加中间件以扩充 ASP.NET 核心请求指标。
  • IHttpMetricsTagsFeature中获取HttpContext。 仅当有人正在侦听指标时,此功能才会出现在上下文中。 使用前,请确认null不是IHttpMetricsTagsFeature
  • 将包含请求的营销源的自定义标记添加到http.server.request.duration指标。
    • 该标记具有名称mkt_medium和基于utm_medium查询字符串值的值。 utm_medium值解析为已知值范围。
    • 该标记允许按营销媒体类型对请求进行分类,这在分析 Web 应用流量时可能很有用。

注意

使用自定义标记进行扩充时,请遵循多维指标最佳做法。 如果标记数量过多或范围不受限制,则会产生许多标记组合,从而导致维度过高。 集合工具对计数器支持的维度有限制,可能会筛选结果以防止内存过多使用。

在某些端点和请求上禁用 HTTP 指标

选择不记录指标对自动化系统(例如运行状况检查)经常调用的端点有好处。 通常不需要记录这些请求的指标。 不必要的遥测数据会占用资源来进行收集和存储,并且会扭曲遥测仪表板上显示的结果。

可以通过添加元数据,使用 DisableHttpMetrics 特性或 DisableHttpMetrics 方法,将发往某个终结点的 HTTP 请求排除在指标收集之外:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();

var app = builder.Build();
app.MapHealthChecks("/healthz").DisableHttpMetrics();
app.Run();

或者,可为以下项添加 IHttpMetricsTagsFeature.MetricsDisabled 属性:

  • 请求未映射到终结点的高级场景。
  • 动态禁用特定 HTTP 请求的指标集合。
// Middleware that conditionally opts-out HTTP requests.
app.Use(async (context, next) =>
{
    var metricsFeature = context.Features.Get<IHttpMetricsTagsFeature>();
    if (metricsFeature != null &&
        context.Request.Headers.ContainsKey("x-disable-metrics"))
    {
        metricsFeature.MetricsDisabled = true;
    }

    await next(context);
});

创建自定义指标

在命名空间中使用 API System.Diagnostics.Metrics 创建指标。 有关信息,请参阅 创建自定义指标

使用 IMeterFactory 在 ASP.NET Core 应用中创建指标

在 ASP.NET Core 应用中使用 Meter 创建 IMeterFactory 实例。

默认情况下,ASP.NET Core 在依赖项注入 (DI) 中注册 IMeterFactory。 计量工厂将指标与 DI 集成,使隔离和收集指标变得简单。 IMeterFactory 对测试非常有用。 它允许多个测试并行运行,并且仅收集测试中记录的指标值。

若要在应用中使用 IMeterFactory,请创建使用 IMeterFactory 创建应用的自定义指标的类型:

public class ContosoMetrics
{
    private readonly Counter<int> _productSoldCounter;

    public ContosoMetrics(IMeterFactory meterFactory)
    {
        var meter = meterFactory.Create("Contoso.Web");
        _productSoldCounter = meter.CreateCounter<int>("contoso.product.sold");
    }

    public void ProductSold(string productName, int quantity)
    {
        _productSoldCounter.Add(quantity,
            new KeyValuePair<string, object?>("contoso.product.name", productName));
    }
}

Program.cs 中向 DI 注册指标类型:

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

根据需要注入指标类型和记录值。 由于指标类型已在 DI 中注册,因此它可与 MVC 控制器、最小 API 或 DI 创建的任何其他类型一起使用:

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

    metrics.ProductSold(model.ProductName, model.QuantitySold);
});

若要监视“Contoso.Web”计数器,请使用以下 dotnet-counters 命令。

dotnet-counters monitor -n WebMetric --counters Contoso.Web

显示了类似下面的输出:

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

[Contoso.Web]
    contoso.product.sold (Count / 1 sec)
        contoso.product.name=Eggs            12    
        contoso.product.name=Milk            0    

使用 OpenTelemetry 和 Prometheus 查看 Grafana 中的指标

Overview

OpenTelemetry:

  • 是一个由云原生计算基金会支持的供应商中立开源项目。
  • 标准化云原生软件的遥测数据生成和收集。
  • 使用 .NET 指标 API 与 .NET 配合使用。
  • 得到 Azure Monitor 和许多 APM 供应商的认可。

从 ASP.NET Core 11 开始,框架的内置 HTTP 服务器指标和跟踪符合 OpenTelemetry HTTP 服务器语义约定所需的部分。 HTTP 服务器请求活动默认发出这些属性,与内置指标匹配。 因此, OpenTelemetry.Instrumentation.AspNetCore NuGet 包是可选的,用于收集 HTTP 服务器指标和跟踪。 本文中的示例仅使用内置计量(Microsoft.AspNetCore.HostingMicrosoft.AspNetCore.Server.Kestrel),并且不引用检测包。 有关内置工具及其属性的列表,请参阅 ASP.NET Core内置 HTTP 指标

虽然该包是可选的,但不能直接替代内置检测。 内置检测仅涵盖语义约定的必需部分。 在删除包之前,请考虑以下差异:

  • 内置检测不会生成某些推荐的 HTTP 服务器属性,例如某些客户端和网络属性(如 client.address)。 对 有条件要求url.query 的属性(包括修订)的完全支持仍在进行中。 如果依赖这些属性,请保留该软件包。 有关详细信息,请参阅 dotnet/aspnetcore#65873
  • 此包也为在 HTTP 服务器之外启用遥测(包括 Blazor 和 SignalR)提供了便利。 在跟踪方面,它会为 SignalR 注册额外的活动源(在 .NET 9 及更高版本中为 Microsoft.AspNetCore.SignalR.Server),并为 Blazor 注册额外的活动源(在 .NET 10 及更高版本中为 Microsoft.AspNetCore.ComponentsMicrosoft.AspNetCore.Components.Server.Circuits)。 对于指标而言,它会启用相应的内置指标采集器(例如 Microsoft.AspNetCore.Components)。 如果不使用该软件包,请自行使用 AddMeter 注册源,并使用 AddSource 注册计量器,以收集相同的遥测数据。

将遥测添加到仅需要 HTTP 服务器指标和跟踪的应用时,可以依赖内置检测并省略包。 将现有应用从 .NET 10 升级到已引用包的 .NET 11 时,如果依赖于前面列表中所述的属性、源或计量,请保留该应用。 移除该软件包会导致相关遥测数据在无提示的情况下丢失。

重要

如果您在未使用 包的情况下启用 OpenTelemetry OpenTelemetry.Instrumentation.AspNetCore(除指标外),请注册框架的 HTTP 服务器 ActivitySource,以便记录请求活动信息。 ASP.NET Core 的 HTTP 服务器活动源名为 Microsoft.AspNetCore,并且框架会为每个请求创建一个名为 Microsoft.AspNetCore.Hosting.HttpRequestIn 的请求活动,以传播跟踪上下文。 如果Microsoft.AspNetCore源未注册到 OpenTelemetry SDK 中,该请求活动就不会被记录,并且默认的ParentBased采样器会静默丢弃在请求期间启动的任何自定义子 span。

在现有跟踪流水线中,显式注册源:

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddSource("Microsoft.AspNetCore")
        .AddSource("MyApp"));

在前面的示例中:

  • AddSource("Microsoft.AspNetCore")注册 ASP.NET Core的 HTTP 服务器活动源,以便记录请求活动。
  • AddSource("MyApp") 注册应用自身的 ActivitySource。 将 MyApp 替换为您的应用所使用的名称。
  • 未显示导出程序。 此示例假定已在跟踪管道中配置了导出程序。

或者,从OpenTelemetry.Instrumentation.AspNetCore包调用AddAspNetCoreInstrumentation(),该包会为你注册源。

本教程使用 OSS PrometheusGrafana 项目展示了可用于 OpenTelemetry 指标的集成之一。 指标数据流:

  1. ASP.NET Core 指标 API 记录示例应用中的度量值。

  2. 在应用中运行的 OpenTelemetry .NET 库将聚合这些度量值。

  3. Prometheus 导出程序库通过 HTTP 指标终结点提供聚合数据。 “导出程序”指的是 OpenTelemetry 调用库来将遥测数据传输到供应商特定的后端。

  4. Prometheus 服务器:

    • 轮询指标终结点。
    • 读取数据。
    • 将数据存储在数据库中以实现长期持久存储。 Prometheus 将读取和存储数据称为抓取终结点。
    • 可以在其他计算机上运行。
  5. Grafana 服务器:

    • 查询 Prometheus 中存储的数据并将其显示在基于 Web 的监控仪表板上。
    • 可以在其他计算机上运行。

查看示例应用中的指标

转到示例应用。 浏览器显示 Hello OpenTelemetry! ticks:<3digits>,其中 3digits 是当前 DateTime.Ticks 的最后三位数字。

/metrics 追加到 URL 以查看指标终结点。 浏览器显示正在收集的指标:

指标 2

设置和配置 Prometheus

按照 Prometheus 起始步骤设置 Prometheus 服务器并确认其正常工作。

修改 prometheus.yml 配置文件,以便 Prometheus 抓取示例应用公开的指标终结点。 在 scrape_configs 部分中添加以下突出显示的文本:

# my global config
global:
  scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
    - static_configs:
        - targets:
          # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: "prometheus"

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
      - targets: ["localhost:9090"]

  - job_name: 'MyASPNETApp'
    scrape_interval: 5s # Poll every 5 seconds for a more responsive demo.
    static_configs:
      - targets: ["localhost:5045"]  ## Enter the HTTP port number of the demo app.

在前面高亮显示的 YAML 中,将 5045 替换为该示例应用使用的端口号。

启动 Prometheus

  1. 重新加载配置或重启 Prometheus 服务器。
  2. 确认 OpenTelemetryTest 在 Prometheus Web 门户的状态>目标页中处于 UP 状态。

Prometheus 状态

选择“打开指标资源管理器”图标以查看可用的指标:

Prometheus open_metric_exp

输入计数器类别,例如 http_表达式 输入框中查看可用的指标:

可用指标

或者,在表达式输入框中输入计数器类别kestrel以查看可用的指标:

Prometheus kestrel

在 Grafana 仪表板上显示指标

dashboard-screenshot2

在 ASP.NET Core 应用中测试指标

可以在 ASP.NET Core应用中测试指标。 执行此操作的一种方法是使用 MetricCollector<T>ASP.NET Core 集成测试来收集和断言指标值。

public class BasicTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;
    public BasicTests(WebApplicationFactory<Program> factory) => _factory = factory;

    [Fact]
    public async Task Get_RequestCounterIncreased()
    {
        // Arrange
        var client = _factory.CreateClient();
        var meterFactory = _factory.Services.GetRequiredService<IMeterFactory>();
        var collector = new MetricCollector<double>(meterFactory,
            "Microsoft.AspNetCore.Hosting", "http.server.request.duration");

        // Act
        var response = await client.GetAsync("/");

        // Assert
        Assert.Contains("Hello OpenTelemetry!", await response.Content.ReadAsStringAsync());

        await collector.WaitForMeasurementsAsync(minCount: 1).WaitAsync(TimeSpan.FromSeconds(5));
        Assert.Collection(collector.GetMeasurementSnapshot(),
            measurement =>
            {
                Assert.Equal("http", measurement.Tags["url.scheme"]);
                Assert.Equal("GET", measurement.Tags["http.request.method"]);
                Assert.Equal("/", measurement.Tags["http.route"]);
            });
    }
}

前面的测试:

  • 使用 WebApplicationFactory<TEntryPoint> 在内存中启动 Web 应用。 工厂泛型参数中的 Program 将指定 Web 应用。
  • 使用 MetricCollector<T> 收集指标值
    • 需要对Microsoft.Extensions.Diagnostics.Testing进行包引用。
    • MetricCollector<T> 是使用 Web 应用的 IMeterFactory 创建的。 这允许收集器仅报告测试记录的指标值。
    • 包括要收集的计量名称 Microsoft.AspNetCore.Hosting 和计数器名称 http.server.request.duration
  • 向 Web 应用发出 HTTP 请求。
  • 使用指标收集器的结果来验证测试。

ASP.NET 核心 Identity 指标

Identity ASP.NET Core可观测性有助于监视用户管理活动和身份验证过程。

指标单位为Microsoft.AspNetCore.Identity米,下文将详细介绍。

用户管理指标

  • aspnetcore.identity.user.create.duration 度量用户创建操作的持续时间。
  • aspnetcore.identity.user.update.duration 度量用户更新操作的时间。
  • aspnetcore.identity.user.delete.duration 度量用户删除操作的持续时间。
  • aspnetcore.identity.user.check_password_attempts 对密码验证尝试进行计数。
  • aspnetcore.identity.user.generated_tokens 统计为用户生成的令牌,例如密码重置令牌。
  • aspnetcore.identity.user.verify_token_attempts 对令牌验证尝试进行计数。

身份验证指标

  • aspnetcore.identity.sign_in.authenticate.duration 度量身份验证操作的持续时间。
  • aspnetcore.identity.sign_in.check_password_attempts 在登录期间对密码检查尝试进行计数。
  • aspnetcore.identity.sign_in.sign_ins 会统计成功登录的次数。
  • aspnetcore.identity.sign_in.sign_outs 计算注销次数。
  • aspnetcore.identity.sign_in.two_factor_clients_remembered 会统计已记住的双因素身份验证客户端数量。
  • aspnetcore.identity.sign_in.two_factor_clients_forgotten 会统计遗忘的双因素身份验证客户端数量。

使用这些指标可以:

  • 监视用户注册和管理。
  • 跟踪身份验证模式和潜在的安全问题。
  • 评估操作的性能 Identity 。
  • 观察双重身份验证使用情况。

查看 Identity 指标

用于 dotnet-counters 查看这些指标并实时监视它们。 或者,使用本文前面所述的技术将其导出到 Prometheus,并在 Grafana 中将其可视化。

例如,若要监视所有 Identity 指标,请使用 dotnet-counters

dotnet-counters monitor -n YourAppName --counters Microsoft.AspNetCore.Identity

ASP.NET Core 计量和计数器

有关 ASP.NET Core计量和计数器的列表,请参阅 ASP.NET Core指标。 在 ASP.NET Core 11 及更高版本中,内置的 HTTP 服务器计量(例如Microsoft.AspNetCore.Hosting,和Microsoft.AspNetCore.Server.Kestrel)发出符合 OpenTelemetry HTTP 服务器语义约定所需部分的数据。 无需 OpenTelemetry.Instrumentation.AspNetCore 包,即可通过 OpenTelemetry SDK 使用这些度量值。