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:
- O código da aplicação chama métodos para incrementar counters, registrar gauges, gravar histograms.
- O SDK OpenTelemetry Metrics coleta essas métricas (em memória) e periodicamente as envia.
- O exporter de métricas envia para o sistema de monitoramento escolhido (Prometheus, Application Insights, Grafana Cloud, Datadog etc.).
- 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):
- Adicionar o pacote NuGet: OpenTelemetry.Exporter.Prometheus.
- Adicionar .AddPrometheusExporter() na configuração de métricas.
- 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.
GO TO FULL VERSION