Cómo modificar un Layout Renderer de NLog

por May 31, 2020Desarrollo de Software0 Comentarios

Hace poco mostraba un wrapper de NLog para tener la funcionalidad de log sin depender de librerías de terceros. Al hacer esto me surgió la necesidad de modificar el comportamiento de uno de los Layout Renderer de NLog.

Introducción

Os pongo en situación. En el proyecto estaba usando como destino de log una base de datos de oracle, y la longitud máxima de los campos de texto en oracle es de 4000 caracteres. En algunas pilas de excepciones el texto excedía de ese límite.

Quería modificar el texto de la excepción pero de una manera limpia. Manteniendo todas las características de NLog y su Layout Renderer de excepciones.

Por ello, hacer un substring en el método que había creado para crear el objeto LogEventInfo no era la mejor opción. Lo ideal sería modificar el comportamiento del propio Layout Renderer de NLog.

Modificar Layout Renderer de Exception en NLog

En la documentación de NLog hay guías para crear nuestros propios Layout Renderers, pero no para modificar los que ya están predefinidos. Podría haber creado uno personalizado para excepciones, pero quería seguir manteniendo las opciones del propio de NLog, como la posibilidad elegir los separadores o diferentes opciones de formato.

Crear Layout Renderer de NLog

Mirando la documentación y viendo el código del Layout Renderer de excepciones se me ocurrió usar la herencia de clases para hacerlo y es funciona perfectamente.

Siguiendo la documentación vemos los principales aspectos necesarios para crear un Layout Renderer.

  • Crear una clase y anotarla con la anotación correspondiente para indicar el nombre del Layout Renderer
  • Sobreescribir el método Append que es el encargado de crear el texto de salida.
  • En mi caso heredar la clase ExceptionLayoutRenderer

Como quiero mantener todas las características del Layout Renderer de exceptiones primero llamo al método Append de la clase padre y después realizo mis modificaciones para reducir el texto.

namespace Lib.Log.NLogLogger
{
    [LayoutRenderer("customException")]
    internal class CustomExceptionNLogLayout : ExceptionLayoutRenderer
    {
        protected override void Append(StringBuilder builder, LogEventInfo logEvent)
        {
            base.Append(builder, logEvent);
            if (builder.Length >= 4000)
            {
                builder.Replace("   ", "");
                builder.Replace("en ", "@");
                builder.Replace("System.Data.Entity", "EF");
                builder.Replace("--- Fin del seguimiento de la pila de la excepción interna ---", "-EndInnerEx-");
                builder.Replace("--->", ">");

                if (builder.Length >= 4000)
                {
                    builder.Length = 3999;
                }
            }
        }
    }
}

Usar el nuevo Layout Renderer

Para usar el Layout Renderer creado simplemente hay que seguir dos pasos en el fichero de configuración de NLog.

En primer lugar hay que añadir el espacio de nombres del compilado que incluye la clase que hemos creado. En mi caso el espacio de nombres de la librería es Lib.Log. Es importante ver que es el espacio de nombres, y no la ruta completa de la clase. Se incluye en la sección extensions del xml de configuración de NLog.

<extensions>
    <add assembly="NLog.Web"/>
    <add assembly="Lib.Log"/>
</extensions>

Después simplemente se usa el Layout Renderer donde sea necesario al configurar el target en el fichero xml de configuración de NLog. Dado que se ha heredado el propio de NLog los parámetros de configuración del Layout Renderer son los mismos que los de ExceptionLayoutRenderer.

Muestro la configuración de un target para base de datos pero resumido, se muestra únicamente el parámetro de la excepción.

<target name="Database" xsi:type="Database" keepConnection="false"
    dbProvider="Oracle.ManagedDataAccess.Client.OracleConnection, Oracle.ManagedDataAccess"
    connectionString="User Id=nombre;Password=pass;Data Source=IP:Port/orcl" commandType="Text">
    <commandText>
        INSERT INTO TABLE_LOG (EXCEPTION) VALUES(:exception_,)
    </commandText>
    <parameter name="exception_" layout="${customException:format=ToString,data:exceptionDataSeparator=|}"/>
</target>

Siguiendo todos los pasos que os he mostrado sería suficiente. Hemos modificado el comportamiento por defecto de un Layout Renderer sin verse afectadas sus características gracias a la herencia de clases. A continuación os dejo un enlace al código en GitHub.