¿Por qué los registradores recomiendan usar un registrador por clase?


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 forma en que opera log4net. ¿Por qué es una buena práctica?

Author: Daniel T., 2010-06-29

10 answers

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

Compare lo siguiente:

Registro 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 para construir un seguimiento de pila para ver quién lo estaba llamando o su código siempre tendría que pasar en la persona que llama. Con el estilo logger-per-class, todavía puedes hacer esto, pero puedes hacerlo una vez por clase en lugar de una vez por llamada y eliminar un problema serio de rendimiento.

 51
Author: Jeremy Wiebe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-29 19:24:20

Ventaja para usar "logger per file" en NLog: tiene la posibilidad de administrar/filtrar registros por espacio de nombres 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 nlogger creará el siguiente código:

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

Así que solo unas pocas pulsaciones de teclado y tienes logger por clase. Utilizará el espacio de nombres y el nombre de la clase como el nombre del registrador. Para establecer un nombre diferente a su registrador de clases, 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á tratando de registrar un mensaje: El nombre del registrador (que generalmente es el nombre de la clase) se puede registrar fácilmente en el archivo (u otro destino) utilizando ${logger} en layout.

 12
Author: CoperNick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-01 20:19:09

Puedo ver algunas razones para esta elección.

  • Siempre sabrá de dónde proviene una instrucción de registro en particular, si incluye el nombre del registrador en su formato de salida de registro.
  • Puede controlar qué instrucciones de registro ve en un nivel de grano fino activando o desactivando ciertos registradores, o configurando su nivel.
 5
Author: Peter Recore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-29 19:16:18

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 logs SQL de Hibernate. Hay un registrador compartido llamado " Hibernate.SQL " o algo así, donde un número de clases diferentes escriben SQL sin procesar en una sola categoría de registrador.

 3
Author: Eric Hauser,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-29 19:16:41

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

Logger logger = LogManager.GetCurrentClassLogger()

Buscar la clase actual de stack trace requiere algo de rendimiento (pero no mucho).

 2
Author: Julian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-11 23:29:59

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 usando reflexión, ralentizará el rendimiento. Para resolver esto, puede usar el siguiente código que crea el registrador de forma dinámica y 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);

        }
    }
}
 2
Author: as9876,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-19 19:45:11

Dos razones vienen inmediatamente a la mente:

  1. Tener un registro separado para cada clase hace que sea fácil agrupar todos los mensajes/errores de registro pertenecientes a una clase dada.
  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.).).
 1
Author: Dan Tao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-29 19:15:44

Probablemente porque desea poder registrar métodos que solo son visibles para la clase sin romper la encapsulación, esto también hace que sea fácil usar la clase en otra aplicación sin romper la funcionalidad de registro.

 0
Author: Rodrick Chapman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-29 19:15:05

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

 0
Author: dotjoe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-29 19:20:06

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

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

Entonces podría usar una constante para el nombre de su registrador o el nombre del ensamblado.

Descargo de responsabilidad: No se cómo NLOG recopila esta información, mi conjetura sería reflexión por lo que puede que tenga que considerar el rendimiento. Hay algunos problemas con los métodos asincrónicos si no está utilizando NLOG v4.4 o posterior.

 0
Author: user6271979,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-24 07:40:35