De acordo com a documentação do NLog:
A maioria dos aplicativos usará um logger por classe, onde o nome do logger é o mesmo que o nome da classe.
É a mesma maneira que o log4net opera. Por que isso é uma boa prática?
De acordo com a documentação do NLog:
A maioria dos aplicativos usará um logger por classe, onde o nome do logger é o mesmo que o nome da classe.
É a mesma maneira que o log4net opera. Por que isso é uma boa prática?
Respostas:
Com o log4net, o uso de um registrador por classe facilita a captura da origem da mensagem de log (ou seja, a gravação da classe no log). Se você não tem um logger por classe, mas em vez disso tem um logger para todo o aplicativo, você precisa recorrer a mais truques de reflexão para saber de onde vêm as mensagens de log.
Compare o seguinte:
using System.Reflection;
private static readonly ILog _logger =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public void SomeMethod()
{
_logger.DebugFormat("File not found: {0}", _filename);
}
Logger.DebugFormat("File not found: {0}", _filename); // Logger determines caller
-- or --
Logger.DebugFormat(this, "File not found: {0}", _filename); // Pass in the caller
Usando o segundo exemplo, o Logger precisaria construir um rastreamento de pilha para ver quem o estava chamando ou seu código sempre teria que passar pelo chamador. Com o estilo logger-per-class, você ainda faz isso, mas pode fazê-lo uma vez por classe em vez de uma vez por chamada e eliminar um sério problema de desempenho.
Vantagem de usar "logger por arquivo" em NLog: você tem a possibilidade de gerenciar / filtrar logs por namespace e nome de classe. Exemplo:
<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" />
O NLogger tem um trecho de código muito útil para fazer isso. O nlogger
snippet criará o seguinte código:
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
Portanto, apenas alguns toques no teclado e você terá um logger por classe. Ele usará o namespace e o nome da classe como o nome do logger. Para definir um nome diferente para o registrador de sua classe, você pode usar isto:
private static NLog.Logger logger = NLog.LogManager.GetLogger("MyLib.MyName");
E, como @JeremyWiebe disse, você não precisa usar truques para obter o nome da classe que está tentando registrar uma mensagem: O nome do logger (que geralmente é o nome da classe) pode ser facilmente registrado no arquivo (ou outro alvo) usando ${logger}
em layout.
Posso ver algumas razões para essa escolha.
Na maioria dos casos, o nome da classe fornece um bom nome para o logger. Ao verificar os arquivos de log, você pode ver a mensagem de log e associá-la diretamente a uma linha de código.
Um bom exemplo onde essa não é a melhor abordagem são os logs SQL do Hibernate. Há um logger compartilhado denominado "Hibernate.SQL" ou algo parecido, onde várias classes diferentes gravam SQL bruto em uma única categoria de logger.
Do ponto de vista do desenvolvimento, é mais fácil se você não precisar criar um objeto logger todas as vezes. Por outro lado, se você não fizer isso, mas sim criá-lo dinamicamente usando reflexão, isso tornará o desempenho mais lento. Para resolver isso, você pode usar o código a seguir, que cria o registrador de forma dinâmica e assí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);
}
}
}
Duas razões vêm imediatamente à mente:
Provavelmente porque você deseja poder registrar métodos que são visíveis apenas para a classe sem quebrar o encapsulamento, isso também torna mais fácil usar a classe em outro aplicativo sem quebrar a funcionalidade de registro.
Se você estiver usando NLOG, você pode especificar o site de chamada na configuração, isso gravará o nome da classe e o método onde a instrução de registro foi localizada.
<property name="CallSite" value="${callsite}" />
Você pode então usar uma constante para o nome do registrador ou o nome do conjunto.
Isenção de responsabilidade: Não sei como o NLOG coleta essas informações, meu palpite seria uma reflexão, portanto, talvez você precise considerar o desempenho. Existem alguns problemas com os métodos Async se você não estiver usando o NLOG v4.4 ou posterior.