Registro en aplicaciones C#: mejores prácticas .NET

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Registro en aplicaciones C#: mejores prácticas y herramientas (.NET)



El registro no es solo escribir mensajes en un archivo. Es un componente esencial de cualquier aplicación de producción en C#. Sin un sistema de registro de calidad, trabajas a ciegas: los errores pasan desapercibidos, el rendimiento es difícil de analizar y la depuración se convierte en un juego de adivinanzas. En este artículo, exploraremos las mejores prácticas de registro en el ecosistema .NET, revisaremos bibliotecas populares (Serilog, NLog), los niveles de registro correctos y un enfoque estructurado para la recopilación de datos.



1. Elección de la biblioteca de registro: Serilog vs NLog



En el mundo .NET existen varias soluciones maduras. Las dos más populares son Serilog y NLog. Ambas admiten registro asíncrono, múltiples sinks (destinos de salida) e integración con .NET Core/5+ a través de ILogger.



Serilog

Serilog es conocido por su filosofía de registro estructurado. En lugar de cadenas simples, pasas objetos y Serilog los serializa a JSON. Esto es ideal para sistemas como Elasticsearch, Seq o Datadog.



// Ejemplo Serilogusing Serilog;

Log.Logger = new LoggerConfiguration() .MinimumLevel.Information() .WriteTo.Console() .WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Day) .CreateLogger();

Log.Information("Usuario {UserId} inició sesión en {Time}", userId, DateTime.UtcNow);


NLog

NLog es una herramienta más tradicional, pero igualmente potente. Es ideal para proyectos que necesitan una configuración flexible a través de XML o código. Admite rotación de archivos, envío por correo electrónico e integración con bases de datos.



// Ejemplo NLogusing NLog;

private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

Logger.Info("Usuario {UserId} realizó una acción", userId);


¿Cuándo elegir qué? Si estás construyendo microservicios o usando el stack ELK, elige Serilog. Si necesitas una configuración sencilla en proyectos legacy, elige NLog. En cualquier caso, ambos admiten el estándar ILogger de Microsoft.Extensions.Logging.



2. Niveles de registro: cuándo y qué escribir



Uno de los errores más comunes es escribir todo en el nivel Information o, por el contrario, poner Debug en todas partes. Usa los niveles correctos:



  • Trace (0) — información extremadamente detallada: entrada/salida de métodos, valores de variables. Se usa solo durante la depuración.
  • Debug (1) — mensajes de depuración útiles para el desarrollador, pero no para el operador.
  • Information (2) — eventos de negocio: "pedido creado", "usuario registrado". Este es el nivel de trabajo principal.
  • Warning (3) — situación anómala que no rompe la aplicación: tiempo de espera de solicitud, reintento de conexión.
  • Error (4) — excepciones manejadas, errores de lógica de negocio, fallos en el funcionamiento de un módulo.
  • Critical (5) — errores fatales: pérdida de conexión con la base de datos, falta de memoria, caída de toda la aplicación.


// Ejemplo de uso correcto de nivelespublic async Task<Order> CreateOrderAsync(CreateOrderRequest request){    Log.Debug("Inicio de creación de pedido para el usuario {UserId}", request.UserId);

try { var order = new Order(request); await _repository.SaveAsync(order); Log.Information("Pedido {OrderId} creado por el usuario {UserId}", order.Id, request.UserId); return order; } catch (DbException ex) { Log.Error(ex, "Error de BD al crear pedido para el usuario {UserId}", request.UserId); throw; // Relanzamos hacia arriba }}


3. Registro estructurado: ¿por qué es importante?



El registro tradicional a través de cadenas de texto simples tiene limitaciones. Con el registro estructurado, cada entrada de registro es un objeto con propiedades con nombre. Esto permite:



  • Búsqueda eficiente — puedes encontrar fácilmente todos los errores para un usuario específico o transacción.
  • Análisis automatizado — herramientas como Elasticsearch, Seq o Datadog indexan campos y crean paneles.
  • Contexto enriquecido — cada entrada contiene no solo el mensaje, sino también metadatos: ID de correlación, nombre del método, versión de la aplicación.


// Ejemplo de registro estructurado con SerilogLog.Information("Pedido {OrderId} procesado. Total: {Total:C}, Método de pago: {PaymentMethod}",     order.Id, order.Total, order.PaymentMethod);

// En el archivo de registro (JSON):// {\"@t\":\"2024-01-15T10:30:00Z\",\"@mt\":\"Pedido {OrderId} procesado...\",\"OrderId\":123,\"Total\":150.00,\"PaymentMethod\":\"Tarjeta\"}


4. Configuración de sinks: dónde almacenar los registros



Un sink es el destino final de tus registros. Las opciones populares en .NET:



Sink Descripción Cuándo usar
Consola Salida a stdout/stderr Desarrollo, contenedores Docker
Archivo Escritura en archivos de texto con rotación Aplicaciones locales, servidores simples
Seq Servidor centralizado de registros con interfaz web Equipos pequeños y medianos
Elasticsearch Almacenamiento en el stack ELK Grandes sistemas, análisis complejos
Application Insights Integración con Azure Monitor Aplicaciones en la nube de Azure


// Ejemplo de configuración de múltiples sinks en SerilogLog.Logger = new LoggerConfiguration()    .MinimumLevel.Information()    .WriteTo.Console()    .WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Day)    .WriteTo.Seq("http://localhost:5341")    .CreateLogger();


5. Registro asíncrono y rendimiento



El registro no debe ralentizar tu aplicación. Tanto Serilog como NLog admiten escritura asíncrona. En Serilog, usa el wrapper Async:



// Registro asíncrono en SerilogLog.Logger = new LoggerConfiguration()    .WriteTo.Async(a => a.File(\"logs/app-.log\", rollingInterval: RollingInterval.Day))    .CreateLogger();


En NLog, la asincronía se configura a través de async en el archivo de configuración:



<targets async=\"true\">    <target name=\"file\" xsi:type=\"File\" fileName=\"logs/app.log\" /></targets>


Importante: No uses el registro en bucles críticos para el rendimiento. Si necesitas registrar cada iteración, usa Log.Verbose o Log.Trace y desactívalos en producción.



6. Enriquecimiento de contexto: añade significado a los registros



Los registros simples sin contexto son difíciles de analizar. Usa enriquecedores para añadir automáticamente información útil:



// Ejemplo de enriquecimiento en SerilogLog.Logger = new LoggerConfiguration()    .Enrich.WithMachineName()    .Enrich.WithThreadId()    .Enrich.WithProperty("Application", "MyApp")    .WriteTo.Console()    .CreateLogger();

// Cada entrada de registro ahora contendrá:// {MachineName: \"SRV-01\", ThreadId: 42, Application: \"MyApp\"}


En NLog, el enriquecimiento se configura a través de layout renderers:



${machinename} ${threadid} ${appdomain:format={1}}


7. Integración con ILogger de Microsoft



La mayoría de las aplicaciones modernas en .NET usan la abstracción ILogger<T> de Microsoft. Tanto Serilog como NLog se integran fácilmente:



// Program.cs (Serilog)builder.Host.UseSerilog((context, config) =>{    config.ReadFrom.Configuration(context.Configuration);});

// En el controladorpublic class OrderController : ControllerBase{ private readonly ILogger<OrderController> _logger;

public OrderController(ILogger<OrderController> logger) { _logger = logger; }

public async Task<IActionResult> CreateOrder(CreateOrderRequest request) { _logger.LogInformation("Creando pedido para el usuario {UserId}", request.UserId); // ... lógica }}


8. Errores comunes en el registro



  1. Registrar datos sensibles — contraseñas, tokens, números de tarjetas de crédito. Usa filtros o enmascáralos.
  2. Demasiados registros — el nivel Debug en producción puede generar gigabytes de datos por hora.
  3. Falta de contexto — registrar "Error al guardar" sin el ID del objeto o la traza de la pila es inútil.
  4. Ignorar excepciones — registrar solo el mensaje sin la excepción pierde información valiosa.
  5. Formato inconsistente — mezclar diferentes formatos de fecha, niveles o estructuras de mensajes.


// Ejemplo de registro incorrectoLog.Error("Error al guardar el pedido"); // ❌ Sin contexto

// Ejemplo de registro correctoLog.Error(ex, "Error al guardar el pedido {OrderId} para el usuario {UserId}", orderId, userId); // ✅


Conclusión



El registro en aplicaciones C# no es un lujo, sino una necesidad. Usar bibliotecas modernas como Serilog o NLog, niveles de registro correctos y un enfoque estructurado te ayudará a:



  • Encontrar y corregir errores rápidamente
  • Analizar el rendimiento de la aplicación
  • Cumplir con los requisitos de auditoría
  • Mantener la cordura durante la depuración nocturna


Comienza con una configuración simple, añade sinks gradualmente y recuerda: el mejor registro es el que te da respuestas, no solo datos.

Blogs

Book Recommendations