¿Cómo dejar de adivinar y empezar a ver tu código de principio a fin?
¿Alguna vez te has sentado frente al monitor por la noche, mirando registros que gritan sobre un error pero guardan silencio sobre la causa? ¿O has intentado entender por qué tu microservicio en C# responde en 10 segundos, cuando ayer volaba todo? ¿Te suena familiar? Bienvenido al mundo donde sin Observabilidad tu código es una caja negra.
En este artículo no daremos conferencias aburridas. Analizaremos los tres pilares de la observabilidad: trazado, métricas y registros — y mostraremos cómo integrarlos con C#. Aprenderás cómo dejar de apagar incendios y empezar a prevenirlos. ¡Allá vamos!
¿Qué es Observabilidad y por qué no es solo "registro"?
Muchos confunden Observabilidad con la simple recolección de registros. Pero la diferencia es colosal. Observabilidad es una propiedad del sistema que te permite hacer cualquier pregunta sobre su estado, sin agregar nuevo código. Los tres pilares sobre los que se sostiene todo:
- Registros (Logs) — eventos discretos. "El usuario presionó un botón", "Error de conexión". Es "¿qué sucedió?".
- Métricas (Metrics) — datos agregados. "Solicitudes por segundo", "Consumo de memoria". Es "¿cuánto y con qué frecuencia?".
- Trazado (Traces) — la ruta de una solicitud a través del sistema. "Llegó la solicitud, fue a la BD, luego a la caché, regresó". Es "¿por qué tarda tanto?".
En el ecosistema C# .NET, estos tres pilares se unen mediante bibliotecas como OpenTelemetry. Y ahora analizaremos cómo se ve esto en el código.
Montamos un experimento: escribimos una aplicación con Observabilidad
Imagina que tenemos un servicio simple en ASP.NET Core que procesa pedidos. Queremos ver:
- Cada paso del procesamiento del pedido (trazado).
- Cuántos pedidos se procesaron y con qué error (métricas).
- Detalles de las excepciones (registros).
Paso 1: Configuración del registro con Serilog
Empecemos por lo más comprensible: los registros. Usaremos Serilog (pero también se puede usar NLog). Es capaz de escribir registros estructurados que luego son fáciles de analizar.
// NuGet: Serilog, Serilog.AspNetCore, Serilog.Sinks.Consoleusing Serilog;
// Configuración en 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(); // Reemplazamos el registrador estándar
// Ejemplo de uso en un controlador[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(); }}Observa que usamos marcadores de posición con nombre ({OrderId}). Esto no es solo texto, son datos estructurados. En Elasticsearch o Grafana Loki podrás buscar todos los registros por un OrderId específico.
Paso 2: Métricas con Prometheus y .NET
Las métricas se encargan de los "números". ¿Cuántas solicitudes por segundo? ¿Cuántos errores 500? En C# usamos Meters de System.Diagnostics.Metrics o la biblioteca prometheus-net.
Creemos un contador simple de pedidos procesados:
// NuGet: prometheus-net.AspNetCoreusing Prometheus;
// En Program.cs agregamos métricasvar builder = WebApplication.CreateBuilder(args);