Estrategia de registro con NLog para entornos de producción en .NET

Transición desde log4net

Es frecuente que, al desplegar aplicaciones .NET en servidores de producción, log4net deje de persistir trazas sin lanzar excepciones visibles. Tras intentar una actualización estándar mediante NuGet:

Install-Package log4net

si el comportamiento se mantiene, la ruta más estable consiste en sustituir el motor por NLog. Esta librería simplifica la definición de destinos y mejora la tolerancia a fallos de configuración.

Instalaicón y estructura base

Reemplace la implementación anterior agregando el núcleo y el esquema de configuración:

Install-Package NLog
Install-Package NLog.Config

Para aplicaciones de escritorio, asegúrese de que el archivo NLog.config tenga la propiedad "Copy to Output Directory" establecida. El siguiente esquema redirige la información a archivos segmentados por fecha y a la consola del sistema:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true" internalLogLevel="Warn">
  <targets>
    <target name="registroArchivos" xsi:type="File" 
            fileName="${basedir}/bitacora/${shortdate}.log"
            layout="${longdate} [${level}] ${message}" />
    <target name="pantalla" xsi:type="ColoredConsole" />
  </targets>
  <rules>
    <logger name="*" minlevel="Info" writeTo="registroArchivos" />
    <logger name="*" minlevel="Warn" writeTo="pantalla" />
  </rules>
</nlog>

Invocación desde código

La instancia del registrador se obtiene automáticamente desde la clase actual. La sintaxis soporta interpolación y niveles jerárquicos:

var gestorTrazas = LogManager.GetCurrentClassLogger();

gestorTrazas.Debug("Módulo de inicialización cargado");
gestorTrazas.Info("Fecha de arranque del servicio: {0}", DateTime.UtcNow);
gestorTrazas.Warn("Umbral de memoria próximo al límite");
gestorTrazas.Error("Fallo en la conexión con el servicio externo");
gestorTrazas.Fatal("Terminación abrupta del hilo principal");

Persistencia en SQL Server para aplicaciones web

Para entornos ASP.NET que requieran almacenar logs en una base de datos relacional, instale el complemento oficial:

Install-Package NLog.Web

Defina el destino de base de datos utilizando procedimientos almacenados para optimizar la inserción masiva:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true" internalLogLevel="Off">
  <targets>
    <target name="almacenRelacional"
            xsi:type="Database"
            connectionStringName="CadenaSQLServer"
            commandType="StoredProcedure"
            commandText="[dbo].[sp_InsertarLogSistema]">
      <parameter name="@nombreHost"    layout="${machinename}" />
      <parameter name="@nombreSitio"   layout="${iis-site-name}" />
      <parameter name="@marcaTiempo"   layout="${date}" />
      <parameter name="@severidad"     layout="${level}" />
      <parameter name="@usuarioActual" layout="${aspnet-user-identity}" />
      <parameter name="@detalle"       layout="${message}" />
      <parameter name="@claseOrigen"   layout="${logger}" />
      <parameter name="@contextoExtra" layout="${all-event-properties:separator=|}" />
      <parameter name="@rutaSolicitud" layout="${aspnet-request:serverVariable=HTTP_URL}" />
    </target>
  </targets>
  <rules>
    <logger name="*" minlevel="Info" writeTo="almacenRelacional" />
  </rules>
</nlog>

Vincule la cadena de conexión en el Web.config de la aplicación:

<configuration>
  <connectionStrings>
    <add name="CadenaSQLServer" connectionString="Server=localhost;Database=LogDB;User Id=admin;Password=clave123;" providerName="System.Data.SqlClient"/>
  </connectionStrings>
</configuration>

Integración con Oracle Database

Para entornos corporativos con Oracle, configure el proveedro de datos adecuado. La versión legacy requiere System.Data.OracleClient, mientras que las implementaciones modernas utilizan Oracle.DataAccess.Client o el proveedor administrado.

<targets>
  <target name="oracleDirecto" type="Database"
          dbProvider="Oracle.DataAccess.Client.OracleConnection,Oracle.DataAccess"
          connectionString="Data Source=ORCL_PROD;User ID=log_user;Password=secure_pass;">
    <commandText>
      INSERT INTO BITACORA_APLICACION (equipo, nivel, fecha_creacion, descripcion) 
      VALUES(:equipo, :nivel, :fecha, :descripcion)
    </commandText>
    <parameter name=":equipo"      layout="${machinename}" />
    <parameter name=":nivel"       layout="${level}" />
    <parameter name=":fecha"       layout="${date}" />
    <parameter name=":descripcion" layout="${message}" />
  </target>
</targets>
<rules>
  <logger name="LogicoOracle" minlevel="Error" writeTo="oracleDirecto" />
</rules>

Ejemplo de consumo específico:

var loggerOracle = LogManager.GetLogger("LogicoOracle");
loggerOracle.Fatal(new InvalidOperationException("Timeout en consulta pesada"), "Operación de exportación fallida");

Soporte para MySQL

Añada la referencia al conector oficial:

Install-Package MySql.Data

La configuración apunta a una tabla dedicada, utilizando parámetros nombrados compatibles con el driver de MySQL:

<targets>
  <target name="motorMySQL" type="Database"
          dbProvider="MySql.Data.MySqlClient.MySqlConnection,MySql.Data"
          connectionString="Server=10.0.0.5;Uid=root;Pwd=dev;Database=logs;">
    <commandText>
      INSERT INTO registro_sistema (host, grado, instante, contenido) 
      VALUES(@host, @grado, @instante, @contenido)
    </commandText>
    <parameter name="@host"      layout="${machinename}" />
    <parameter name="@grado"     layout="${level}" />
    <parameter name="@instante"  layout="${date}" />
    <parameter name="@contenido" layout="${message}" />
  </target>
</targets>
<rules>
  <logger name="*" minlevel="Debug" writeTo="motorMySQL" />
</rules>

Formato avanzado de excepciones en archivos

Para garantizar que las trazas de pila se almacenen correctamente junto al mensaje, utilice un renderizador específico en la propiedad layout:

<target name="registroDetallado" xsi:type="File" 
        fileName="${basedir}/errores/${shortdate}.txt"
        layout="${longdate} | ${level} | ${logger} | ${message} | ${exception:format=tostring}" />

Almacenamiento en MongoDB

Para arquitecturas NoSQL, instale la extensión comunitaria:

Install-Package NLog.Mongo

Registre la extensión en la cabecera del archivo y defina el documento BSON. Se recomienda limitar el crecimianto de la colección mediante un capped collection para evitar saturación de disco:

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <extensions>
    <add assembly="NLog.Mongo"/>
  </extensions>
  <targets>
    <target xsi:type="Mongo" name="coleccionNoSQL"
            connectionString="mongodb://192.168.1.10:27017/BitacoraSistema"
            collectionName="EventosEjecucion"
            cappedCollectionSize="31457280">
      <property name="IdHilo" layout="${threadid}" bsonType="Int32" />
      <property name="IdProceso" layout="${processid}" bsonType="Int32" />
      <property name="RutaEjecutable" layout="${processname:fullName=true}" />
      <property name="CuentaWindows" layout="${windows-identity}" />
      <property name="MensajeSistema" layout="${message}" />
    </target>
  </targets>
  <rules>
    <logger name="*" level="Info" writeTo="coleccionNoSQL" />
  </rules>
</nlog>

Etiquetas: NLog .NET CSharp logging sql-server

Publicado el 9-3 04:12