Como parar de adivinhar e começar a ver seu código por completo?
Você já se sentou à noite em frente ao monitor, olhando para logs que gritam sobre um erro, mas se calam sobre a causa? Ou tentou entender por que seu microsserviço em C# está respondendo em 10 segundos, embora ontem tudo voasse? Familiar? Bem-vindo ao mundo onde sem Observabilidade seu código é uma caixa preta.
Neste artigo, não vamos ler palestras chatas. Vamos analisar os três pilares da observabilidade: rastreamento, métricas e logs — e mostrar como integrá-los com C#. Você aprenderá como parar de apagar incêndios e começar a preveni-los. Vamos lá!
O que é Observabilidade e por que não é apenas "registro de logs"?
Muitos confundem Observabilidade com a simples coleta de logs. Mas a diferença é colossal. Observabilidade é uma propriedade do sistema que permite fazer qualquer pergunta sobre seu estado, sem adicionar novo código. Três pilares que sustentam tudo:
- Logs — eventos discretos. "Usuário clicou no botão", "Erro de conexão". É "o que aconteceu?".
- Métricas — dados agregados. "Requisições por segundo", "Consumo de memória". É "quanto e com que frequência?".
- Rastreamento (Traces) — caminho da requisição pelo sistema. "Requisição chegou, foi ao BD, depois ao cache, retornou". É "por que está tão lento?".
No ecossistema C# .NET, esses três pilares são unidos com bibliotecas como OpenTelemetry. E agora vamos ver como isso se parece no código.
Montando um experimento: escrevendo uma aplicação com Observabilidade
Imagine que temos um serviço simples em ASP.NET Core que processa pedidos. Queremos ver:
- Cada etapa do processamento do pedido (rastreamento).
- Quantos pedidos foram processados e com qual erro (métricas).
- Detalhes das exceções (logs).
Passo 1: Configuração de logs com Serilog
Vamos começar pelo mais simples — logs. Usamos Serilog (mas também pode ser NLog). Ele consegue escrever logs estruturados, que depois são fáceis de analisar.
// NuGet: Serilog, Serilog.AspNetCore, Serilog.Sinks.Consoleusing Serilog;
// Configuração em Program.csLog.Logger = new LoggerConfiguration() .WriteTo.Console() .WriteTo.File("logs/app.log", rollingInterval: RollingInterval.Day) .Enrich.WithProperty("Application", "OrderService") .CreateLogger();
var builder = WebApplication.CreateBuilder(args);builder.Host.UseSerilog(); // Substitui o logger padrão
// Exemplo de uso em um controller[ApiController][Route("api/[controller]")]public class OrderController : ControllerBase{ private readonly ILogger<OrderController> _logger;
public OrderController(ILogger<OrderController> logger) => _logger = logger;
[HttpPost] public IActionResult CreateOrder(Order order) { _logger.LogInformation("Creating order {OrderId} for user {User}", order.Id, order.UserId); // ... lógica ... _logger.LogWarning("Order {OrderId} is delayed due to stock issues", order.Id); return Ok(); }}Observe: usamos placeholders nomeados ({OrderId}). Isso não é apenas texto — são dados estruturados. No Elasticsearch ou Grafana Loki, você poderá pesquisar todos os logs por um OrderId específico.
Passo 2: Métricas com Prometheus e .NET
As métricas são responsáveis pelos "números". Quantas requisições por segundo? Quantos erros 500? Em C#, usamos Meters do System.Diagnostics.Metrics ou a biblioteca prometheus-net.
Vamos criar um contador simples de pedidos processados:
// NuGet: prometheus-net.AspNetCoreusing Prometheus;
// Em Program.cs adicionamos métricasvar builder = WebApplication.CreateBuilder(args);