Perché i logger consigliano di utilizzare un logger per classe?


88

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?


1
hmm. sembra che ci siano due problemi qui: uno è avere un oggetto log effettivo per classe e uno è avere il nome del log uguale a quello della classe.
Peter Recore

Risposte:


59

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:

Registro per classe

using System.Reflection;
private static readonly ILog _logger = 
    LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);    

public void SomeMethod()
{
    _logger.DebugFormat("File not found: {0}", _filename);
}

Un logger per app (o simile)

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.


Grazie, questo aiuta a chiarire le cose. Stavamo solo inserendo manualmente il nome della classe e il metodo nel messaggio (ad esempio "ImageCreator.CreateThumbnail () chiamato"), ma è meglio se il logger può gestirlo.
Daniel T.

1
Per tua informazione, è diventata una pratica "migliore" avere un Logger per istanza, piuttosto che per classe (cioè statica) perché ciò rende più facile acquisire informazioni come le informazioni sui thread. Ovviamente è una questione di gusti, nessuna "regola rigida e veloce", ma volevo buttarla via.
Will Hartung

7
@ Will, puoi spiegarlo un po 'di più? Quando si accede utilizzando un logger per classe, registro sempre l'ID thread in modo che il logger possa ottenere le informazioni sul thread corrente. Qualsiasi altra informazione sul thread sarebbe disponibile anche per il logger.
Jeremy Wiebe

@ Jeremy Wiebe: è questo l'unico motivo? Funzionalmente non ci sono problemi se utilizzo una singola variabile globale di tipo logger per l'intera app?
giorgim

1
@Giorgi No, non credo proprio. È possibile ottenere molte di queste informazioni in questi giorni con gli attributi CallerInformation che rendono l'unico logger per classe un po 'meno rilevante - msdn.microsoft.com/en-us/library/hh534540.aspx
Jeremy Wiebe

15

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 nloggersnippet 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.


5

Posso vedere alcuni motivi per questa scelta.

  • Saprai sempre da dove proviene una particolare istruzione di log, se includi il nome del logger nel formato di output del log.
  • È possibile controllare quali istruzioni di registro visualizzare a un livello granulare attivando o disattivando determinati logger o impostando il loro livello.

4

C'è anche un vantaggio in termini di prestazioni nel caso di NLog. La maggior parte degli utenti utilizzerà

Logger logger = LogManager.GetCurrentClassLogger()

La ricerca della classe corrente dall'analisi dello stack richiede alcune (ma non molte) prestazioni.


3

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.


2

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

        }
    }
}

1

Mi vengono subito in mente due ragioni:

  1. Avere un registro separato per ogni classe semplifica il raggruppamento di tutti i messaggi / errori di registro relativi a una determinata classe.
  2. Avere un registro all'interno di una classe consente di registrare dettagli interni che potrebbero non essere accessibili al di fuori della classe (ad esempio, stato privato, informazioni relative all'implementazione di una classe, ecc.).

2
Hai un logger nella classe indipendentemente dal fatto che sia definito a livello di classe o globalmente. I logger globali non sono "fuori" dalla classe dal punto di vista della visibilità. Stai ancora facendo riferimento al logger globale dall'interno della classe in questione in modo da avere piena visibilità.
Robert

0

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.


1
Rende difficile usare quella classe in un'altra applicazione. Devi fare riferimento alla libreria di registrazione che ti piaccia o no.
Piotr Perak

0

Semplifica la configurazione degli appendici in base allo spazio dei nomi o alla classe.


0

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.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.