CodeGym /Cursos /C# SELF /Monitoramento de desempenho e coleta de métricas

Monitoramento de desempenho e coleta de métricas

C# SELF
Nível 64 , Lição 3
Disponível

1. Introdução

Imagine que você é o administrador de um site, e todo dia chegam mil usuários. Nos logs você vê que tudo funciona, quase não há erros (talvez alguém esqueça a senha ou erre o captcha de vez em quando). "Tudo ótimo!" — você pensa.

E de repente alguém escreve no suporte: o site está muito lento, as páginas demoram 10 segundos para abrir. Você olha os logs — não há erros! Ufa? Não: logs contam o que aconteceu (ou não aconteceu), mas não dizem quão rápido ou devagar isso foi, quantos recursos foram usados, e como esse comportamento mudou com o aumento da carga.

É aí que entram as métricas — características mensuráveis do funcionamento da aplicação. Não é só o número de erros, mas também o tempo médio de resposta, o volume de memória usado, requisições por segundo e outros indicadores que mostram a saúde do sistema.

Comparação:
Logs — são "o que aconteceu".
Métricas — são "quão bem/mal o sistema está funcionando".
Tracing — é "por que o sistema funciona assim (em detalhes)".

Que tipos de métricas existem e o que vale a pena coletar?

Principais tipos de métricas:

Tipo de métrica Exemplo Para que serve
Counters Número de requisições, erros, falhas Tendências, alertas, carga
Histograms Tempo de resposta, tamanho do pacote Distribuição de valores, percentis
Gauges Uso de memória, CPU Estado atual do recurso
Sum Volume total de dados, bytes Volume total de operações por período

Exemplos:

  • Média e o percentil 95 do tempo de resposta de uma requisição GET.
  • Número de usuários online agora mesmo.
  • Uso de memória (Private Bytes, Working Set).
  • Frequência de erros tipo 500/503.
  • Queries ao DB por minuto.

Esses indicadores permitem não só encontrar problemas, mas também preveni-los — porque carga aumentada ou um crescimento gradual do tempo de resposta podem sinalizar falhas futuras.

2. Como funciona a coleta de métricas no .NET e no ecossistema OpenTelemetry

Arquitetura geral

No .NET moderno (a partir do .NET 6, especialmente no .NET 8/9) existe um sistema padrão de coleta de métricas baseado em OpenTelemetry.

Aqui está como funciona:

  1. O código da aplicação chama métodos para incrementar counters, registrar gauges, gravar histograms.
  2. O SDK OpenTelemetry Metrics coleta essas métricas (em memória) e periodicamente as envia.
  3. O exporter de métricas envia para o sistema de monitoramento escolhido (Prometheus, Application Insights, Grafana Cloud, Datadog etc.).
  4. O backend de monitoramento agrega, armazena, visualiza, cria alertas e dashboards.
Diagrama esquemático:

[Seu aplicativo] 
       ⬇ 
 [Coleta de métricas (OpenTelemetry SDK)]
       ⬇
 [Exporter de métricas (Prometheus, AI, Datadog, ...)]
       ⬇
 [Sistema de monitoramento/dashboards/alertas]

3. Prática: fundamentos do trabalho com métricas em C#

Métricas internas simples: System.Diagnostics.Metrics

O .NET fornece um mecanismo embutido para métricas — System.Diagnostics.Metrics.

Jogadores principais: Meter, Counter<T>, Histogram<T>, ObservableGauge<T>.

Exemplo: contador de visitas à página


// Criamos o Meter (normalmente um por aplicação)
using System.Diagnostics.Metrics;

static Meter meter = new Meter("MyCompany.MyApp", "1.0");

// Registramos o counter
static Counter<long> homePageVisits = meter.CreateCounter<long>("HomePageVisits");

// Em algum lugar no controller ou serviço...
public void HomePageRequested()
{
    homePageVisits.Add(1);
    // Resto do código que trata a página
}

Comentários:

  • Meter é a "fábrica" de métricas, com um nome único (namespace da aplicação/empresa).
  • CreateCounter<long> cria um counter; incrementa-se com Add(1).

Exemplo: medir tempo de resposta


static Histogram<double> pageLoadTime = meter.CreateHistogram<double>("PageLoadTimeMs");

// No handler de requisição:
public void OnRequest()
{
    var stopwatch = System.Diagnostics.Stopwatch.StartNew();

    // ...a própria requisição...

    stopwatch.Stop();
    pageLoadTime.Record(stopwatch.Elapsed.TotalMilliseconds);
}

Coletando gauges para valores dinâmicos

Gauge é uma métrica que varia com o tempo: número de usuários conectados, volume atual de memória etc.


static ObservableGauge<int> onlineUserGauge = meter.CreateObservableGauge(
    "OnlineUsers", 
    () => GetOnlineUserCount());

// Onde GetOnlineUserCount é um método que retorna o valor atual
static int GetOnlineUserCount()
{
    // Aqui deve estar sua lógica real!
    return ActiveUserList.Count;
}

No mundo real isso roda de forma assíncrona: a aplicação fornece valores das métricas, e o exporter as coleta e envia (por exemplo, Prometheus scrapa o endpoint "/metrics").

Adicionando métricas em uma aplicação ASP.NET Core moderna

No ASP.NET Core muita coisa já vem pronta. Basta adicionar o pacote OpenTelemetry.Instrumentation.AspNetCore, e você terá métricas de HTTP, tempos de resposta, número de erros etc.

Exemplo de configuração em Program.cs:


using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics =>
    {
        metrics
            .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MyApp"))
            .AddAspNetCoreInstrumentation() // métricas HTTP
            .AddRuntimeInstrumentation()    // métricas do runtime .NET CLR
            .AddProcessInstrumentation()    // CPU/memória do processo
            .AddMeter("MyCompany.MyApp")    // suas métricas
            .AddPrometheusExporter();       // exporta para Prometheus
    });

var app = builder.Build();

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

app.Run();

Agora sua aplicação vai expor métricas no caminho /metrics, que podem ser scrapes pelo Prometheus ou por outros sistemas.

4. Exemplos práticos de uso de métricas

Monitorando desempenho em projetos reais

Conectamos métricas para descobrir:

  • Qual o RPS médio e de pico que a API aguenta?
  • Onde estão os gargalos: um endpoint responde ~300 ms, outro ~2000 ms?
  • Quanto tempo é gasto em chamadas ao DB? (adicionamos nossos próprios histograms)

Exemplo: monitorar tempo de resposta ao banco


static Histogram<double> dbQueryDuration = meter.CreateHistogram<double>("DbQueryDurationMs");

public async Task<List<Product>> GetProductsAsync()
{
    var sw = Stopwatch.StartNew();
    var result = await _db.Products.ToListAsync();
    sw.Stop();
    dbQueryDuration.Record(sw.Elapsed.TotalMilliseconds);
    return result;
}

Exemplo: contar número de erros


static Counter<long> apiErrors = meter.CreateCounter<long>("ApiErrors");

public IActionResult SomeEndpoint()
{
    try
    {
        // alguma ação
        return Ok();
    }
    catch (Exception)
    {
        apiErrors.Add(1);
        throw;
    }
}

Trabalhando com labels (tags) para métricas

É importante agrupar dados por dimensões úteis: endpoint, tipo de erro, tipo de usuário, etc.


homePageVisits.Add(
    1, 
    KeyValuePair.Create<string, object>("UserType", "Admin"));

Ou para um histogram:


dbQueryDuration.Record(
    sw.Elapsed.TotalMilliseconds, 
    KeyValuePair.Create<string, object>("QueryType", "GetProducts"));

Graças às tags no Grafana você consegue montar gráficos não só para a aplicação inteira, mas também por segmentos específicos.

5. Integração com Prometheus, Application Insights, Datadog, Grafana

Exporters e integração

  • Prometheus — monitoramento open-source popular, padrão de fato para cloud e Kubernetes.
  • Application Insights — integração em nuvem para Azure.
  • Datadog, Grafana Cloud — para infra profissional.

Todos esses sistemas podem coletar métricas do .NET via exporters do OpenTelemetry. Documentação sobre exporters OTel

Prometheus (passos):

  1. Adicionar o pacote NuGet: OpenTelemetry.Exporter.Prometheus.
  2. Adicionar .AddPrometheusExporter() na configuração de métricas.
  3. No Grafana configurar o datasource apontando para o Prometheus e criar dashboards.

Links úteis:

6. Particularidades, armadilhas e erros típicos

Um erro comum é a excessiva granularidade das tags. Se você der muitas tags com valores únicos (por exemplo, userId/orderId), o número de series temporais explode — isso sobrecarrega o armazenamento de métricas e aumenta custos (a chamada cardinality explosion). Mantenha tags "coarse-grained".

Desenvolvedores às vezes ignoram o System.Diagnostics.Metrics e ferramentas prontas, reinventando a roda com logs e timers. No final o monitoramento fica menos integrado e mais difícil de manter. Use as ferramentas padrão e instrumentação automática.

Outra cilada é métricas coletadas mas não exportadas. A configuração do exporter é obrigatória: adicione, por exemplo, .AddPrometheusExporter() e verifique se o endpoint /metrics está acessível para scraping.

E por fim, confusão entre tipos de métricas: média de tempo de resposta calculada com Counter, quando deveria usar Histogram — então você não verá picos e distribuição. Counters servem para contagens; histograms para tempos/tamanhos/distribuições; gauges para estados atuais.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION