Come da documentazione di NLog:
La maggior parte delle applicazioni utilizzerà un logger per classe, dove il nome del logger è lo stesso del nome della classe.
Questo è lo stesso modo in cui funziona log4net. Perché è una buona pratica?
Come da documentazione di NLog:
La maggior parte delle applicazioni utilizzerà un logger per classe, dove il nome del logger è lo stesso del nome della classe.
Questo è lo stesso modo in cui funziona log4net. Perché è una buona pratica?
Risposte:
Con log4net, l'utilizzo di un logger per classe semplifica l'acquisizione dell'origine del messaggio di log (ovvero la classe che scrive nel log). Se non si dispone di un logger per classe, ma si dispone invece di un logger per l'intera app, è necessario ricorrere a più trucchi di riflessione per sapere da dove provengono i messaggi di registro.
Confronta i seguenti:
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
Utilizzando il secondo esempio, il Logger avrebbe bisogno di creare un'analisi dello stack per vedere chi lo stava chiamando, altrimenti il tuo codice dovrebbe sempre passare al chiamante. Con lo stile logger per classe, puoi ancora farlo, ma puoi farlo una volta per classe invece che una volta per chiamata ed eliminare un grave problema di prestazioni.
Vantaggio nell'usare "logger per file" in NLog: hai la possibilità di gestire / filtrare i log per spazio dei nomi e nome della classe. Esempio:
<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 ha uno snippet di codice molto utile per farlo. Lo nlogger
snippet creerà il codice seguente:
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
Quindi solo poche sequenze di tasti e hai logger per classe. Userà lo spazio dei nomi e il nome della classe come nome del logger. Per impostare un nome diverso per il tuo registratore di classe, puoi usare questo:
private static NLog.Logger logger = NLog.LogManager.GetLogger("MyLib.MyName");
E, come ha detto @JeremyWiebe, non devi usare trucchi per ottenere il nome della classe che sta cercando di registrare un messaggio: il nome del logger (che di solito è il nome della classe) può essere facilmente registrato su file (o altro target) utilizzando ${logger}
nel layout.
Posso vedere alcuni motivi per questa scelta.
Nella maggior parte dei casi, il nome della classe fornisce un buon nome per il logger. Durante la scansione dei file di registro, è possibile visualizzare il messaggio di registro e associarlo direttamente a una riga di codice.
Un buon esempio in cui questo non è l'approccio migliore sono i log SQL di Hibernate. Esiste un logger condiviso denominato "Hibernate.SQL" o qualcosa del genere, in cui un numero di classi differenti scrive l'SQL grezzo in una singola categoria di logger.
Dal punto di vista dello sviluppo, è più semplice se non devi creare ogni volta un oggetto logger. D'altra parte, se non lo fai, ma piuttosto lo crei dinamicamente usando la riflessione, rallenterà le prestazioni. Per risolvere questo problema, puoi utilizzare il seguente codice che crea il logger in modo dinamico e asincrono:
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);
}
}
}
Mi vengono subito in mente due ragioni:
Probabilmente perché si desidera essere in grado di registrare metodi visibili solo alla classe senza interrompere l'incapsulamento, questo semplifica anche l'utilizzo della classe in un'altra applicazione senza interrompere la funzionalità di registrazione.
Se stai usando NLOG puoi specificare il callsite nella configurazione, questo registrerà il nome della classe e il metodo in cui si trovava l'istruzione di registrazione.
<property name="CallSite" value="${callsite}" />
È quindi possibile utilizzare una costante per il nome del logger o il nome dell'assembly.
Dichiarazione di non responsabilità: non so come NLOG raccolga queste informazioni, la mia ipotesi sarebbe una riflessione, quindi potrebbe essere necessario considerare le prestazioni. Ci sono alcuni problemi con i metodi Async se non stai usando NLOG v4.4 o successivo.