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

88

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 esta una buena práctica?

Daniel T.
fuente
1
hmm. Parece que hay dos problemas aquí: uno tiene un objeto de registro real por clase y el otro tiene el nombre del registro es el mismo que el de la clase.
Peter Recore

Respuestas:

59

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 un registrador para toda la aplicación, debe recurrir a más trucos de reflexión para saber de dónde provienen 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 construir un seguimiento de pila para ver quién lo estaba llamando o su código siempre tendría que pasar a la persona que llama. Con el estilo de registrador por clase, aún lo hace, pero puede hacerlo una vez por clase en lugar de una vez por llamada y eliminar un problema de rendimiento grave.

Jeremy Wiebe
fuente
Gracias, eso ayuda a aclarar las cosas. Simplemente estábamos poniendo manualmente el nombre de la clase y el método en el mensaje (es decir, "ImageCreator.CreateThumbnail () llamado"), pero es mejor si el registrador puede manejarlo.
Daniel T.
1
Para su información, se ha convertido en una práctica "mejor" tener un registrador por instancia, en lugar de por clase (es decir, estático) porque eso hace que sea más fácil capturar información como la información del hilo. Obviamente es una cuestión de gustos, no una "regla estricta y rápida", pero quería descartar eso.
Will Hartung
7
@will, ¿puedes explicar eso un poco más? Cuando registro con un registrador por clase, siempre registro el ID del hilo para que el registrador pueda obtener la información actual del hilo. Cualquier otra información del hilo también estaría disponible para el registrador.
Jeremy Wiebe
@Jeremy Wiebe: ¿Es esta la única razón? Funcionalmente, ¿no hay problemas si uso una única variable global de tipo logger para toda la aplicación?
giorgim
1
@Giorgi No, no lo creo. Puede obtener mucha de esta información estos días con los atributos de CallerInformation, lo que hace que el registrador por clase sea un poco menos relevante: msdn.microsoft.com/en-us/library/hh534540.aspx
Jeremy Wiebe
15

Ventaja de usar "registrador por archivo" 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 nloggerfragmento creará el siguiente código:

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

Tan solo unas pocas pulsaciones de teclas y tienes registrador por clase. Utilizará el espacio de nombres y el nombre de la clase como 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) puede registrarse fácilmente en el archivo (u otro objetivo) mediante el uso ${logger}en el diseño.

CoperNick
fuente
5

Puedo ver algunas razones para esta elección.

  • Siempre sabrá de dónde proviene 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 detallado activando o desactivando determinados registradores, o configurando su nivel.
Peter Recore
fuente
4

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 del seguimiento de la pila requiere algo de rendimiento (pero no mucho).

Julian
fuente
3

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 en el que este no es el mejor enfoque son los registros SQL de Hibernate. Hay un registrador compartido llamado "Hibernate.SQL" o algo por el estilo, donde varias clases diferentes escriben SQL sin formato en una sola categoría de registrador.

Eric Hauser
fuente
2

Desde el punto de vista del desarrollo, es más fácil si no tiene que crear un objeto de registro cada vez. Por otro lado, si no lo hace, sino que lo crea dinámicamente utilizando la reflexión, ralentizará el rendimiento. Para resolver esto, puede usar el siguiente código que crea el registrador de forma dinámica y asincrónica:

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);

        }
    }
}
as9876
fuente
1

Inmediatamente me vienen a la mente dos razones:

  1. Tener un registro separado para cada clase hace que sea fácil agrupar todos los mensajes / errores de registro 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.).
Dan Tao
fuente
2
Tiene un registrador en la clase independientemente de si está definido a nivel de clase o globalmente. Los madereros globales no están "fuera" de la clase desde una perspectiva de visibilidad. Todavía está haciendo referencia al registrador global desde dentro de la clase en cuestión para que tenga una visibilidad completa.
Robert
0

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.

Rodrick Chapman
fuente
1
Hace que sea difícil usar esa clase en otra aplicación. Tienes que hacer referencia a la biblioteca de registro, te guste o no.
Piotr Perak
0

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

dotjoe
fuente
0

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

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

A continuación, puede utilizar una constante para el nombre de su registrador o el nombre del ensamblado.

Descargo de responsabilidad: no sé cómo NLOG recopila esta información, supongo que sería una 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.

usuario6271979
fuente