Métricas de rede no .NET
Métricas são medidas numéricas reportadas ao longo do tempo. Eles geralmente são usados para monitorar a integridade de um aplicativo e gerar alertas.
A partir do .NET 8, os componentes System.Net.Http
e System.Net.NameResolution
são instrumentados para publicar métricas usando a nova API System.Diagnostics.Metrics da .NET.
Essas métricas foram projetadas em cooperação com OpenTelemetry para garantir que sejam consistentes com o padrão e funcionem bem com ferramentas populares como Prometheus e Grafana.
Eles também são multidimensional, o que significa que as medições estão associadas a pares chave-valor chamados tags (também conhecidos como atributos ou rótulos). As tags permitem a categorização da medição para ajudar na análise.
Dica
Para obter uma lista abrangente de todos os instrumentos internos, juntamente com seus atributos, consulte System.Net métricas.
Recolher métricas System.Net
Para aproveitar a instrumentação de métricas interna, um aplicativo .NET precisa ser configurado para coletar essas métricas. Isso normalmente significa transformá-los para armazenamento e análise externos, por exemplo, em sistemas de monitoramento.
Há várias maneiras de coletar métricas de rede no .NET.
- Para obter uma visão geral rápida usando um exemplo simples e independente, consulte Coletar métricas com dotnet-counters.
- Para coleta e monitoramento de métricas de de tempo de produção, você pode usar o Grafana com OpenTelemetry e Prometheus ou Azure Monitor Application Insights. No entanto, essas ferramentas podem ser inconvenientes de usar no momento do desenvolvimento devido à sua complexidade.
- Para coleta de métricas de e solução de problemas em tempo de desenvolvimento, recomendamos o uso do .NET Aspire, que fornece uma maneira simples, mas extensível, de iniciar métricas e rastreamento distribuído em seu aplicativo e diagnosticar problemas localmente.
- Também é possível reutilizar o projeto Aspire Service Defaults sem a orquestração do Aspire , que é uma forma prática de introduzir as APIs de configuração de métricas e rastreio OpenTelemetry no seu projeto ASP.NET.
Colete métricas com dotnet-counters
dotnet-counters
é uma ferramenta de linha de comando multiplataforma para exame ad-hoc de métricas .NET e investigação de desempenho de primeiro nível.
Para este tutorial, crie uma aplicação que envie pedidos HTTP para vários endpoints em paralelo.
dotnet new console -o HelloBuiltinMetrics
cd ..\HelloBuiltinMetrics
Substitua o conteúdo do Program.cs
pelo seguinte código de exemplo:
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."); }
});
}
Certifique-se de que dotnet-counters
está instalado:
dotnet tool install --global dotnet-counters
Inicie o aplicativo HelloBuiltinMetrics.
dotnet run -c Release
Inicie dotnet-counters
em uma janela de CLI separada e especifique o nome do processo e os medidores a serem observados e, em seguida, pressione uma tecla no aplicativo HelloBuiltinMetrics para que ele comece a enviar solicitações. Assim que as medições começam a chegar, o dotnet-counters
atualiza continuamente a consola com os números mais recentes.
dotnet-counters monitor --counters System.Net.Http,System.Net.NameResolution -n HelloBuiltinMetrics
Colete métricas com o .NET Aspire
Uma maneira simples de coletar rastreamentos e métricas em aplicativos ASP.NET é usar .NET Aspire. O .NET Aspire é um conjunto de extensões para o .NET para facilitar a criação e o trabalho com aplicativos distribuídos. Um dos benefícios de usar o .NET Aspire é que a telemetria é incorporada, usando as bibliotecas OpenTelemetry para .NET.
Os modelos de projeto padrão para o .NET Aspire contêm um projeto ServiceDefaults
. Cada serviço na solução .NET Aspire tem uma referência ao projeto Service Defaults. Os serviços usam-no para instalar e configurar o OTel.
O modelo de projeto Service Defaults inclui os pacotes OTel SDK, ASP.NET, HttpClient e Runtime Instrumentation. Esses componentes de instrumentação são configurados no arquivo Extensions.cs. Para suportar a visualização por telemetria no Aspire Dashboard, o projeto Service Defaults também inclui o exportador OTLP por predefinição.
O Aspire Dashboard foi concebido para trazer a observação de telemetria para o ciclo de depuração local, o que permite aos programadores garantir que as aplicações estão a produzir telemetria. A visualização por telemetria também ajuda a diagnosticar esses aplicativos localmente. Ser capaz de observar as chamadas entre serviços é tão útil no momento da depuração quanto na produção. O painel do .NET Aspire é iniciado automaticamente quando você F5 o projeto AppHost
do Visual Studio ou dotnet run
o projeto AppHost
da linha de comando.
Passo a passo rápido
Crie um do .NET Aspire 9 Starter App usando
dotnet new
:dotnet new aspire-starter-9 --output AspireDemo
Ou no Visual Studio, crie um novo projeto e selecione o template .NET Aspire 9 Starter App:
Abra
Extensions.cs
no projetoServiceDefaults
e role até o métodoConfigureOpenTelemetry
. Observe a chamadaAddHttpClientInstrumentation()
a subscrever os medidores de rede..WithMetrics(metrics => { metrics.AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddRuntimeInstrumentation(); })
Observe que no .NET 8+,
AddHttpClientInstrumentation()
pode ser substituído por assinaturas de medidores manuais:.WithMetrics(metrics => { metrics.AddAspNetCoreInstrumentation() .AddMeter("System.Net.Http") .AddMeter("System.Net.NameResolution") .AddRuntimeInstrumentation(); })
Execute o projeto
AppHost
. Isto deve iniciar o Painel de Controlo Aspire.Navegue até a página Meteorologia do aplicativo
webfrontend
para gerar uma solicitação deHttpClient
paraapiservice
. Atualize a página várias vezes para enviar várias solicitações.Volte para o Dashboard, navegue até a página de Métricas e selecione o recurso
webfrontend
. Ao descer a página, você deve ser capaz de navegar pelas métricas incorporadas deSystem.Net
.
Para obter mais informações sobre o .NET Aspire, consulte:
- Visão geral do Aspire
- Telemetria no Aspire
- Aspire Dashboard
Reutilizar projeto de Padrões de Serviço sem orquestração do .NET Aspire
O projeto Aspire Service Defaults fornece uma maneira fácil de configurar o OTel para projetos ASP.NET, mesmo que não use o resto do .NET Aspire como o AppHost para orquestração. O projeto Service Defaults está disponível como um modelo de projeto via Visual Studio ou dotnet new
. Ele configura OTel e configura o exportador OTLP. Em seguida, você pode usar as variáveis de ambiente OTel para configurar o ponto de extremidade OTLP para enviar telemetria e fornecer as propriedades do recurso para o aplicativo.
As etapas para usar ServiceDefaults fora do .NET Aspire são:
Adicione o ServiceDefaults projeto à solução usando Adicionar Novo Projeto no Visual Studio ou use
dotnet new
:dotnet new aspire-servicedefaults --output ServiceDefaults
Faça referência ao projeto ServiceDefaults a partir do seu aplicativo ASP.NET. No Visual Studio, selecione Adicionar> de referência de projeto e selecione o ServiceDefaults projeto"
Chame a função de configuração OpenTelemetry
ConfigureOpenTelemetry()
como parte da inicialização do construtor de aplicativos.var builder = WebApplication.CreateBuilder(args) builder.ConfigureOpenTelemetry(); // Extension method from ServiceDefaults. var app = builder.Build(); app.MapGet("/", () => "Hello World!"); app.Run();
Para obter um passo a passo completo, consulte Exemplo: Usar OpenTelemetry com OTLP e o Painel de Instrumentos Aspire autónomo.
Veja métricas no Grafana com OpenTelemetry e Prometheus
Para ver como conectar um aplicativo de exemplo com Prometheus e Grafana, siga o passo a passo em Usando OpenTelemetry com Prometheus, Grafana e Jaeger.
Para sobrecarregar HttpClient
enviando pedidos paralelos para vários endpoints, expanda a aplicação de exemplo com o seguinte endpoint:
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.";
});
Crie um painel do Grafana selecionando o ícone + na barra de ferramentas superior e, após, selecionando Painel. No editor de painel exibido, digite Open HTTP/1.1 Connections na caixa Título e a seguinte consulta no campo de expressão PromQL:
sum by(http_connection_state) (http_client_open_connections{network_protocol_version="1.1"})
Selecione Aplicar para salvar e visualizar o novo painel. Ele exibe o número de conexões HTTP/1.1 ativas vs ociosas no pool.
Enriquecimento
Enriquecimento é a adição de tags personalizadas (também conhecidas como atributos ou rótulos) a uma métrica. Isso é útil se um aplicativo quiser adicionar uma categorização personalizada a painéis ou alertas criados com métricas.
O instrumento http.client.request.duration
apoia o enriquecimento registrando retornos de chamada com o HttpMetricsEnrichmentContext.
Observe que esta é uma API de baixo nível e um registo de callback separado é necessário para cada HttpRequestMessage
.
Uma maneira simples de fazer o registo de callback num único local é implementar um DelegatingHandlercustomizado. Isso permite intercetar e modificar as solicitações antes que elas sejam encaminhadas para o manipulador interno e enviadas para o 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);
}
}
Se estiver a trabalhar com IHttpClientFactory
, pode utilizar AddHttpMessageHandler para registar o 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");
Observação
Por motivos de desempenho, a chamada de enriquecimento só é invocada quando o instrumento http.client.request.duration
está ativado, o que indica que algum processo deve estar a recolher as métricas.
Pode ser dotnet-monitor
, exportador de Prometheus, um MeterListener
ou um MetricCollector<T>
.
IMeterFactory
e IHttpClientFactory
integração
As métricas HTTP foram projetadas com isolamento e capacidade de teste em mente. Esses aspetos são suportados pelo uso de IMeterFactory, que permite a publicação de métricas por uma instância de Meter personalizada, a fim de manter os medidores isolados uns dos outros.
Por padrão, um Meter global é usado para emitir todas as métricas. Isto é Meter interno à biblioteca System.Net.Http
. Esse comportamento pode ser substituído atribuindo uma instância de IMeterFactory personalizada a SocketsHttpHandler.MeterFactory ou HttpClientHandler.MeterFactory.
Observação
O Meter.Name é System.Net.Http
para todas as métricas emitidas por HttpClientHandler
e SocketsHttpHandler
.
Ao trabalhar com Microsoft.Extensions.Http
e IHttpClientFactory
no .NET 8+, a implementação de IHttpClientFactory
padrão seleciona automaticamente a instância de IMeterFactory
registrada no IServiceCollection e a atribui ao manipulador primário que cria internamente.
Observação
A partir do .NET 8, o método AddHttpClient chama automaticamente AddMetrics para inicializar os serviços de métricas e registrar a implementação de IMeterFactory padrão com IServiceCollection. O armazenamento em cache padrão do IMeterFactory guarda instâncias Meter por nome, o que significa que existe um Meter com o nome System.Net.Http
por IServiceCollection.
Métricas de teste
O exemplo a seguir demonstra como validar métricas internas em testes de unidade usando xUnit, IHttpClientFactory
e MetricCollector<T>
do pacote NuGet 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 vs. Contadores de Eventos
As métricas são mais ricas em funcionalidades do que os Contadores de Eventos, sobretudo devido à sua natureza multidimensional. Essa multidimensionalidade permite criar consultas sofisticadas em ferramentas como o Prometheus e obter insights em um nível que não é possível com o EventCounters.
No entanto, a partir do .NET 8, apenas os componentes System.Net.Http
e System.Net.NameResolutions
são instrumentados usando métricas, o que significa que, se você precisar de contadores dos níveis inferiores da pilha, como System.Net.Sockets
ou System.Net.Security
, deverá usar EventCounters.
Além disso, existem algumas diferenças semânticas entre Metrics e seus EventCounters correspondentes.
Por exemplo, ao usar HttpCompletionOption.ResponseContentRead
, o current-requests
EventCounter considera uma solicitação ativa até o momento em que o último byte do corpo da solicitação foi lido.
Sua métrica homóloga http.client.active_requests
não inclui o tempo gasto lendo o corpo da resposta ao contar as solicitações ativas.
Precisa de mais métricas?
Se você tiver sugestões para outras informações úteis que possam ser expostas por meio de métricas, crie um problema de dotnet/runtime.