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.NameResolution
se 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
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
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:
Abra
Extensions.cs
en el proyecto deServiceDefaults
y desplácese hasta el métodoConfigureOpenTelemetry
. Observe la llamadaAddHttpClientInstrumentation()
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(); })
Ejecute el proyecto
AppHost
. Esto debería lanzar el Aspire Dashboard.Navegue a la página Weather de la aplicación
webfrontend
para generar una solicitudHttpClient
haciaapiservice
. Actualice la página varias veces para enviar varias solicitudes.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 deSystem.Net
.
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:
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
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"
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.
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, IHttpClientFactory
y 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.