logger log library implement c# log4net logging nlog

c# - log - ¿Por qué los madereros recomiendan usar un registrador por clase?



implement nlog in c# (10)

Con log4net, el uso de un registrador por clase facilita la captura de la fuente del mensaje de registro (es decir, la clase que escribe en el registro). Si no tiene un registrador por clase, sino que tiene un registrador para toda la aplicación, necesita recurrir a más trucos de reflexión para saber de dónde provienen los mensajes de registro.

Compare lo siguiente:

Log por clase

using System.Reflection; private static readonly ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); public void SomeMethod() { _logger.DebugFormat("File not found: {0}", _filename); }

Un registrador por aplicación (o similar)

Logger.DebugFormat("File not found: {0}", _filename); // Logger determines caller -- or -- Logger.DebugFormat(this, "File not found: {0}", _filename); // Pass in the caller

Usando el segundo ejemplo, el Registrador necesitaría construir un seguimiento de pila para ver quién lo llamaba o su código siempre tendría que pasar a la persona que llama. Con el estilo de logger por clase, aún lo hace, pero puede hacerlo una vez por clase en lugar de una vez por llamada y eliminar un problema grave de rendimiento.

Según la documentación de NLog:

La mayoría de las aplicaciones usarán un registrador por clase, donde el nombre del registrador es el mismo que el nombre de la clase.

Esta es la misma manera que funciona log4net. ¿Por qué es esta una buena práctica?


Desde el punto de vista del desarrollo, es más fácil si no tiene que crear un objeto logger cada vez. Por otro lado, si no lo haces, sino que lo creas dinámicamente mediante la reflexión, se ralentizará el rendimiento. Para solucionar esto, puede usar el siguiente código que crea el registrador dinámicamente de forma asíncrona:

using NLog; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WinForms { class log { public static async void Log(int severity, string message) { await Task.Run(() => LogIt(severity, message)); } private static void LogIt(int severity, string message) { StackTrace st = new StackTrace(); StackFrame x = st.GetFrame(2); //the third one goes back to the original caller Type t = x.GetMethod().DeclaringType; Logger theLogger = LogManager.GetLogger(t.FullName); //https://github.com/NLog/NLog/wiki/Log-levels string[] levels = { "Off", "Trace", "Debug", "Info", "Warn", "Error", "Fatal" }; int level = Math.Min(levels.Length, severity); theLogger.Log(LogLevel.FromOrdinal(level), message); } } }


Dos razones inmediatamente vienen a la mente:

  1. Tener un registro separado para cada clase hace que sea fácil agrupar todos los mensajes de registro / errores pertenecientes a una clase determinada.
  2. Tener un registro dentro de una clase le permite registrar detalles internos que pueden no ser accesibles fuera de la clase (por ejemplo, estado privado, información relacionada con la implementación de una clase, etc.).

En la mayoría de los casos, el nombre de la clase proporciona un buen nombre para el registrador. Al escanear los archivos de registro, puede ver el mensaje de registro y asociarlo directamente con una línea de código.

Un buen ejemplo donde este no es el mejor enfoque, son los registros SQL de Hibernate. Hay un registrador compartido llamado "Hibernate.SQL" o algo así, donde varias clases diferentes escriben SQL sin formato en una única categoría de registrador.


Facilita la configuración de los appenders por espacio de nombres o clase.


Probablemente porque desea poder registrar métodos que solo son visibles para la clase sin romper la encapsulación, esto también facilita el uso de la clase en otra aplicación sin romper la funcionalidad de registro.


Puedo ver algunas razones para esta elección.

  • Siempre sabrá de dónde viene una declaración de registro en particular, si incluye el nombre del registrador en su formato de salida de registro.
  • Puede controlar qué declaraciones de registro ve en un nivel de grano fino activando o desactivando ciertos registradores o configurando su nivel.

Si está utilizando NLOG puede especificar callsite en la configuración, esto registrará el nombre de la clase y el método donde se encuentra la declaración de registro.

<property name="CallSite" value="${callsite}" />

Luego puede usar una constante para su nombre de registrador o el nombre del ensamblado.

Descargo de responsabilidad: no sé cómo NLOG recopila esta información, supongo que sería reflexión, por lo que es posible que deba considerar el rendimiento. Hay algunos problemas con los métodos Async si no está utilizando NLOG v4.4 o posterior.


También hay un beneficio de rendimiento en el caso de NLog. La mayoría de los usuarios usarán

Logger logger = LogManager.GetCurrentClassLogger()

Al buscar la clase actual desde el seguimiento de la pila, se obtiene cierto rendimiento (pero no mucho).


Ventaja para usar "logger por archivo" en NLog: tiene la posibilidad de administrar / filtrar registros por nombre de espacio y nombre de clase. Ejemplo:

<logger name="A.NameSpace.MyClass" minlevel="Debug" writeTo="ImportantLogs" /> <logger name="A.NameSpace.MyOtherClass" minlevel="Trace" writeTo="ImportantLogs" /> <logger name="StupidLibrary.*" minlevel="Error" writeTo="StupidLibraryLogs" /> <!-- Hide other messages from StupidLibrary --> <logger name="StupidLibrary.*" final="true" /> <!-- Log all but hidden messages --> <logger name="*" writeTo="AllLogs" />

NLogger tiene un fragmento de código muy útil para hacer esto. El fragmento de nlogger creará el siguiente código:

private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

Entonces, solo unas pocas teclas y usted tiene logger por clase. Utilizará el espacio de nombres y el nombre de clase como el nombre del registrador. Para establecer un nombre diferente para su logger de clase, puede usar esto:

private static NLog.Logger logger = NLog.LogManager.GetLogger("MyLib.MyName");

Y, como dijo @JeremyWiebe, no tiene que usar trucos para obtener el nombre de la clase que está intentando registrar un mensaje: el nombre del registrador (que generalmente es el nombre de la clase) puede ser fácil de registrar en el archivo (u otro objetivo) usando ${logger} en el diseño.