Compartir a través de


Métricas de red en .NET

Métricas son mediciones numéricas reportadas a lo largo del tiempo. Normalmente se usan para supervisar el estado de una aplicación y generar alertas.

A partir de .NET 8, los componentes System.Net.Http y System.Net.NameResolutionse instrumentan para publicar métricas mediante la nueva API System.Diagnostics.Metrics de .NET. Estas métricas se diseñaron en cooperación con openTelemetry para asegurarse de que son coherentes con el estándar y funcionan bien con herramientas populares como Prometheus y Grafana. También son multidimensionales, lo que significa que los medidos están asociados a pares clave-valor denominados etiquetas (también conocidas como atributos). Las etiquetas permiten la categorización de la medida para ayudar al análisis.

Sugerencia

Para obtener una lista completa de todos los instrumentos integrados junto con sus atributos, consulte Métricas de System.Net.

Recopilación de métricas de System.Net

Para aprovechar la instrumentación de métricas integradas, es necesario configurar una aplicación de .NET para recopilar estas métricas. Normalmente, esto significa transformarlos para el almacenamiento y el análisis externos, por ejemplo, para supervisar sistemas.

Hay varias maneras de recopilar métricas de red en .NET.

  • Para obtener una visión general rápida con un ejemplo sencillo e independiente, consulte Recolectar métricas con dotnet-counters.
  • Para la recopilación y supervisión de métricas en tiempo de producción, puede utilizar Grafana con OpenTelemetry y Prometheus o Azure Monitor Application Insights. Sin embargo, estas herramientas pueden ser inconvenientes de usar en tiempo de desarrollo debido a su complejidad.
  • Para la recopilación de métricas y la solución de problemas en tiempo de desarrollo, recomendamos utilizar .NET Aspire, que proporciona una forma sencilla pero extensible de poner en marcha métricas y rastreo distribuido en su aplicación y diagnosticar problemas localmente.
  • También es posible reutilizar el proyecto Aspire Service Defaults sin la orquestación Aspire, que es una forma práctica de introducir las API de configuración de métricas y seguimiento de OpenTelemetry en su proyecto ASP.NET.

Recopila métricas con dotnet-counters

dotnet-counters es una herramienta de línea de comandos multiplataforma para el examen ad hoc de las métricas de .NET y la investigación de rendimiento de primer nivel.

Para este tutorial, cree una aplicación que envíe solicitudes HTTP a varios puntos de conexión en paralelo.

dotnet new console -o HelloBuiltinMetrics
cd ..\HelloBuiltinMetrics

Reemplace el contenido de Program.cs por el código de ejemplo siguiente:

using System.Net;

string[] uris = ["http://example.com", "http://httpbin.org/get", "https://example.com", "https://httpbin.org/get"];
using HttpClient client = new()
{
    DefaultRequestVersion = HttpVersion.Version20
};

Console.WriteLine("Press any key to start.");
Console.ReadKey();

while (!Console.KeyAvailable)
{
    await Parallel.ForAsync(0, Random.Shared.Next(20), async (_, ct) =>
    {
        string uri = uris[Random.Shared.Next(uris.Length)];
        try
        {
            byte[] bytes = await client.GetByteArrayAsync(uri, ct);
            await Console.Out.WriteLineAsync($"{uri} - received {bytes.Length} bytes.");
        }
        catch { await Console.Out.WriteLineAsync($"{uri} - failed."); }
    });
}

Asegúrese de que dotnet-counters está instalado:

dotnet tool install --global dotnet-counters

Inicie la aplicación HelloBuiltinMetrics.

dotnet run -c Release

Inicie dotnet-counters en una ventana de la CLI independiente y especifique el nombre del proceso y los medidores que se van a ver y, a continuación, presione una tecla en la aplicación HelloBuiltinMetrics para que empiece a enviar solicitudes. En cuanto las medidas empiecen a aterrizar, dotnet-counters actualiza continuamente la consola con los números más recientes:

dotnet-counters monitor --counters System.Net.Http,System.Net.NameResolution -n HelloBuiltinMetrics

salida dotnet-counters

Recopilación de métricas con .NET Aspire

Una manera sencilla de recopilar trazas y métricas en aplicaciones ASP.NET es usar .NET Aspire. .NET Aspire es un conjunto de extensiones en .NET para facilitar la creación y el trabajo con aplicaciones distribuidas. Una de las ventajas de usar .NET Aspire es que la telemetría está integrada mediante las bibliotecas de OpenTelemetry para .NET.

Las plantillas de proyecto predeterminadas para .NET Aspire contienen un proyecto de ServiceDefaults. Cada servicio de la solución .NET Aspire tiene una referencia al proyecto Service Defaults. Los servicios lo utilizan para instalar y configurar OTel.

La plantilla de proyecto Service Defaults incluye el SDK de OTel, ASP.NET, HttpClient y paquetes de instrumentación en tiempo de ejecución. Estos componentes de instrumentación se configuran en el archivo Extensions.cs. El proyecto Service Defaults también incluye de forma predeterminada el exportador de OTLP para admitir la visualización de telemetría en el panel Aspire.

Aspire Dashboard está diseñado para llevar la observación de telemetría al ciclo de depuración local, lo que permite a los desarrolladores asegurarse de que las aplicaciones producen telemetría. La visualización de telemetría también ayuda a diagnosticar esas aplicaciones localmente. Poder observar las llamadas entre servicios es tan útil en tiempo de depuración como en producción. El panel de control de .NET Aspire se inicia automáticamente cuando se realiza el comando F5 el proyecto con AppHost desde Visual Studio o dotnet run el proyecto con AppHost desde la línea de comandos.

Tutorial rápido

  1. Cree una .NET Aspire 9 Starter App utilizando dotnet new:

    dotnet new aspire-starter-9 --output AspireDemo
    

    O en Visual Studio, cree un nuevo proyecto y seleccione la plantilla .NET Aspire 9 Starter App:

    Crear una aplicación de inicio de .NET Aspire 9 en Visual Studio

  2. Abra Extensions.cs en el proyecto de ServiceDefaults y desplácese hasta el método ConfigureOpenTelemetry. Observe la llamada AddHttpClientInstrumentation() suscribiéndose a los contadores de red.

    .WithMetrics(metrics =>
    {
        metrics.AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddRuntimeInstrumentation();
    })
    

    Tenga en cuenta que, en .NET 8+, AddHttpClientInstrumentation() se puede reemplazar por suscripciones de medidor manual:

    .WithMetrics(metrics =>
    {
        metrics.AddAspNetCoreInstrumentation()
            .AddMeter("System.Net.Http")
            .AddMeter("System.Net.NameResolution")
            .AddRuntimeInstrumentation();
    })
    
  3. Ejecute el proyecto AppHost. Esto debería lanzar el Aspire Dashboard.

  4. Navegue a la página Weather de la aplicación webfrontend para generar una solicitud HttpClient hacia apiservice. Actualice la página varias veces para enviar varias solicitudes.

  5. Vuelva al Dashboard, vaya a la página de métricas y seleccione el recurso webfrontend. Al desplazarse hacia abajo, debería poder examinar las métricas incorporadas de System.Net.

    métricas de red en Aspire Dashboard

Para obtener más información sobre .NET Aspire, consulte:

Reutilización del proyecto de configuración predeterminada del servicio sin orquestación de .NET Aspire

El proyecto Aspire Service Defaults proporciona una manera sencilla de configurar OTel para proyectos de ASP.NET, incluso si no se usa el resto de .NET Aspire como AppHost para orquestación. El proyecto Service Defaults está disponible como plantilla de proyecto a través de Visual Studio o dotnet new. Configura OTel y configura el exportador OTLP. A continuación, puede usar las variables de entorno de OTel para configurar el punto de conexión de OTLP para enviar telemetría y proporcionar las propiedades de recursos para la aplicación.

Los pasos para usar ServiceDefaults fuera de .NET Aspire son:

  1. Agregue el proyecto ServiceDefaults a la solución mediante Agregar nuevo proyecto en Visual Studio o use dotnet new:

    dotnet new aspire-servicedefaults --output ServiceDefaults
    
  2. Haga referencia al proyecto ServiceDefaults desde la aplicación de ASP.NET. En Visual Studio, seleccione Agregar>Referencia de proyecto y seleccione el proyecto ServiceDefaults"

  3. Llame a la función de configuración de OpenTelemetry ConfigureOpenTelemetry() como parte de la inicialización de su compilador de aplicaciones.

    var builder = WebApplication.CreateBuilder(args)
    builder.ConfigureOpenTelemetry(); // Extension method from ServiceDefaults.
    var app = builder.Build();
    app.MapGet("/", () => "Hello World!");
    app.Run();
    

Para un tutorial completo, consulte Ejemplo: Utilizar OpenTelemetry con OTLP y el Dashboard independiente de Aspire.

Visualización de métricas en Grafana con OpenTelemetry y Prometheus

Para ver cómo conectar una aplicación de ejemplo con Prometheus y Grafana, siga el tutorial de Uso de OpenTelemetry con Prometheus, Grafana y Jaeger.

Para resaltar HttpClient mediante el envío de solicitudes paralelas a varios puntos de conexión, extienda la aplicación de ejemplo con el siguiente punto de conexión:

app.MapGet("/ClientStress", async Task<string> (ILogger<Program> logger, HttpClient client) =>
{
    string[] uris = ["http://example.com", "http://httpbin.org/get", "https://example.com", "https://httpbin.org/get"];
    await Parallel.ForAsync(0, 50, async (_, ct) =>
    {
        string uri = uris[Random.Shared.Next(uris.Length)];

        try
        {
            await client.GetAsync(uri, ct);
            logger.LogInformation($"{uri} - done.");
        }
        catch { logger.LogInformation($"{uri} - failed."); }
    });
    return "Sent 50 requests to example.com and httpbin.org.";
});

Para crear un panel de control de Grafana, seleccione el icono + en la barra de herramientas superior y, a continuación, seleccione Panel de Control. En el editor del panel que aparece, escriba Abrir conexiones HTTP/1.1 en el cuadro Título y la siguiente consulta en el campo de expresión PromQL:

sum by(http_connection_state) (http_client_open_connections{network_protocol_version="1.1"})

Seleccione Aplicar para guardar y ver el nuevo panel. Muestra el número de conexiones HTTP/1.1 activas frente a inactivas en el conjunto.

Conexiones HTTP/1.1 en Grafana

Enriquecimiento

Enriquecimiento es la adición de etiquetas personalizadas (también conocidas como atributos o labels) a una métrica. Esto resulta útil si una aplicación quiere agregar una categorización personalizada a paneles o alertas compiladas con métricas. El instrumento http.client.request.duration admite el enriquecimiento mediante el registro de devoluciones de llamada con HttpMetricsEnrichmentContext. Tenga en cuenta que se trata de una API de bajo nivel y se necesita un registro de devolución de llamada independiente para cada HttpRequestMessage.

Una manera sencilla de realizar el registro de devolución de llamada en un solo lugar es implementar DelegatingHandler personalizado. Esto le permite interceptar y modificar las solicitudes antes de que se reenvíen al controlador interno y se envíen al servidor:

using System.Net.Http.Metrics;

using HttpClient client = new(new EnrichmentHandler() { InnerHandler = new HttpClientHandler() });

await client.GetStringAsync("https://httpbin.org/response-headers?Enrichment-Value=A");
await client.GetStringAsync("https://httpbin.org/response-headers?Enrichment-Value=B");

sealed class EnrichmentHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpMetricsEnrichmentContext.AddCallback(request, static context =>
        {
            if (context.Response is not null) // Response is null when an exception occurs.
            {
                // Use any information available on the request or the response to emit custom tags.
                string? value = context.Response.Headers.GetValues("Enrichment-Value").FirstOrDefault();
                if (value != null)
                {
                    context.AddCustomTag("enrichment_value", value);
                }
            }
        });
        return base.SendAsync(request, cancellationToken);
    }
}

Si está trabajando con IHttpClientFactory, puede utilizar AddHttpMessageHandler para registrar el EnrichmentHandler:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Net.Http.Metrics;

ServiceCollection services = new();
services.AddHttpClient(Options.DefaultName).AddHttpMessageHandler(() => new EnrichmentHandler());

ServiceProvider serviceProvider = services.BuildServiceProvider();
HttpClient client = serviceProvider.GetRequiredService<HttpClient>();

await client.GetStringAsync("https://httpbin.org/response-headers?Enrichment-Value=A");
await client.GetStringAsync("https://httpbin.org/response-headers?Enrichment-Value=B");

Nota

Por motivos de rendimiento, el callback de enriquecimiento solo se invoca cuando el instrumento http.client.request.duration está habilitado, lo que significa que algo debería recopilar las métricas. Puede ser dotnet-monitor, el exportador de Prometheus, MeterListener o MetricCollector<T>.

Integración de IMeterFactory y IHttpClientFactory

Las métricas HTTP se diseñaron teniendo en cuenta el aislamiento y la capacidad de prueba. Estos aspectos se respaldan en el uso de IMeterFactory, que permite publicar métricas mediante una instancia de Meter personalizada con el fin de mantener los medidores aislados entre sí. De forma predeterminada, se usa un Meter global para emitir todas las métricas. Este Meter es interno a la biblioteca System.Net.Http. Este comportamiento se puede invalidar asignando una instancia de IMeterFactory personalizada a SocketsHttpHandler.MeterFactory o HttpClientHandler.MeterFactory.

Nota

El Meter.Name es System.Net.Http para todas las métricas emitidas por HttpClientHandler y SocketsHttpHandler.

Al trabajar con Microsoft.Extensions.Http y IHttpClientFactory en .NET 8+, la implementación de IHttpClientFactory predeterminada selecciona automáticamente la instancia de IMeterFactory registrada en el IServiceCollection y la asigna al controlador principal que crea internamente.

Nota

A partir de .NET 8, el método AddHttpClient llama automáticamente a AddMetrics para inicializar los servicios de métricas y registrar la implementación de IMeterFactory predeterminada con IServiceCollection. El IMeterFactory por defecto almacena en caché Meter instancias por nombre, lo que significa que hay un Meter con el nombre System.Net.Http por IServiceCollection.

Métricas de prueba

En el ejemplo siguiente se muestra cómo validar las métricas integradas en pruebas unitarias mediante xUnit, IHttpClientFactoryy MetricCollector<T> desde el paquete NuGet de Microsoft.Extensions.Diagnostics.Testing:

[Fact]
public async Task RequestDurationTest()
{
    // Arrange
    ServiceCollection services = new();
    services.AddHttpClient();
    ServiceProvider serviceProvider = services.BuildServiceProvider();
    var meterFactory = serviceProvider.GetService<IMeterFactory>();
    var collector = new MetricCollector<double>(meterFactory,
        "System.Net.Http", "http.client.request.duration");
    var client = serviceProvider.GetRequiredService<HttpClient>();

    // Act
    await client.GetStringAsync("http://example.com");

    // Assert
    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"]);
        });
}

Métricas frente a EventCounters

Las métricas cuentan con más características que EventCounters, especialmente debido a su naturaleza multidimensional. Esta multidimensionalidad le permite crear consultas sofisticadas en herramientas como Prometheus y obtener información sobre un nivel que no es posible con EventCounters.

Sin embargo, a partir de .NET 8, solo los componentes System.Net.Http y System.Net.NameResolutions se instrumentan mediante Métricas, lo que significa que si necesita contadores de los niveles inferiores de la pila, como System.Net.Sockets o System.Net.Security, debe usar EventCounters.

Además, hay algunas diferencias semánticas entre las métricas y sus eventCounters coincidentes. Por ejemplo, al usar HttpCompletionOption.ResponseContentRead, el current-requests EventCounter considera que una solicitud está activa hasta el momento en que se ha leído el último byte del cuerpo de la solicitud. Su homólogo de métricas http.client.active_requests no incluye el tiempo dedicado a leer el cuerpo de la respuesta al contar las solicitudes activas.

¿Necesita más métricas?

Si tiene alguna sugerencia relativa a otra información de utilidad que podría exponerse a través de métricas, cree una incidencia de dotnet/runtime.