Hmm. parece que há dois problemas aqui - um é ter um objeto de log real por classe e outro é ter o nome do log igual ao da classe.
Peter Recore
Respostas:
59
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:
Log por aula
using System.Reflection;privatestaticreadonlyILog _logger =LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);publicvoidSomeMethod(){
_logger.DebugFormat("File not found: {0}", _filename);}
Um logger por aplicativo (ou 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 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.
Obrigado, isso ajuda a esclarecer as coisas. Estávamos apenas colocando manualmente o nome da classe e o método na mensagem (ou seja, "ImageCreator.CreateThumbnail () chamado"), mas é melhor se o logger puder lidar com isso.
Daniel T.
1
Apenas para sua informação, tornou-se uma prática "melhor" ter um Logger por instância, em vez de por classe (ou seja, estático), porque isso torna mais fácil capturar informações, como informações de thread. Obviamente é uma questão de gosto, nenhuma "regra dura e rápida", mas eu queria apenas jogar isso fora.
Will Hartung de
7
@will, você pode explicar um pouco mais? Ao registrar usando um Logger por classe, sempre registro o ID do thread para que o logger possa obter as informações do thread atual. Qualquer outra informação de thread estaria disponível para o logger também.
Jeremy Wiebe
@Jeremy Wiebe: Esse é o único motivo? Funcionalmente, não há problemas se eu usar uma única variável global do tipo logger para todo o aplicativo?
giorgim
1
@Giorgi Não, acho que não. Você pode obter muitas dessas informações hoje em dia com os atributos CallerInformation, o que torna um registrador por classe um pouco menos relevante - msdn.microsoft.com/en-us/library/hh534540.aspx
Jeremy Wiebe
15
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 fromStupidLibrary--><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 nloggersnippet criará o seguinte código:
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:
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.
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
{publicstaticasyncvoidLog(int severity,string message){awaitTask.Run(()=>LogIt(severity, message));}privatestaticvoidLogIt(int severity,string message){StackTrace st =newStackTrace();StackFrame x = st.GetFrame(2);//the third one goes back to the original callerType t = x.GetMethod().DeclaringType;Logger theLogger =LogManager.GetLogger(t.FullName);//https://github.com/NLog/NLog/wiki/Log-levelsstring[] levels ={"Off","Trace","Debug","Info","Warn","Error","Fatal"};int level =Math.Min(levels.Length, severity);
theLogger.Log(LogLevel.FromOrdinal(level), message);}}}
Ter um log separado para cada classe torna mais fácil agrupar todas as mensagens / erros de log pertencentes a uma determinada classe.
Ter um log dentro de uma classe permite que você registre detalhes internos que podem não estar acessíveis fora da classe (por exemplo, estado privado, informações que lidam com a implementação de uma classe, etc.).
Você tem um registrador na classe, independentemente de ele ser definido no nível da classe ou globalmente. Loggers globais não estão "fora" da classe de uma perspectiva de visibilidade. Você ainda está fazendo referência ao logger global de dentro da classe em questão, para que tenha visibilidade total.
Robert
0
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.
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:
Log por aula
Um logger por aplicativo (ou similar)
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.
fonte
Vantagem de usar "logger por arquivo" em NLog: você tem a possibilidade de gerenciar / filtrar logs por namespace e nome de classe. Exemplo:
O NLogger tem um trecho de código muito útil para fazer isso. O
nlogger
snippet criará o seguinte código: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:
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.fonte
Posso ver algumas razões para essa escolha.
fonte
Também há um benefício de desempenho no caso do NLog. A maioria dos usuários usará
Pesquisar a classe atual a partir do rastreamento de pilha leva algum (mas não muito) desempenho.
fonte
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.
fonte
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:
fonte
Duas razões vêm imediatamente à mente:
fonte
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.
fonte
Facilita a configuração de appenders por namespace ou classe.
fonte
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.
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.
fonte