it-swarm.com.de

Warum empfehlen Logger die Verwendung eines Loggers pro Klasse?

Gemäß NLog-Dokumentation:

Die meisten Anwendungen verwenden einen Logger pro Klasse, wobei der Name des Loggers mit dem Namen der Klasse übereinstimmt.

Dies ist die gleiche Funktionsweise von log4net. Warum ist das eine gute Praxis?

80
Daniel T.

Mit log4net ist es mit einem Logger pro Klasse leicht, die Quelle der Protokollnachricht zu erfassen (dh die Klasse, die in das Protokoll schreibt). Wenn Sie nicht über einen Logger pro Klasse verfügen, stattdessen jedoch einen Logger für die gesamte App, müssen Sie weitere Reflektionstricks verwenden, um zu erfahren, woher die Protokollnachrichten kommen. 

Vergleichen Sie folgendes:

Protokoll pro Klasse

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

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

Ein Logger pro App (oder ähnlich)

Logger.DebugFormat("File not found: {0}", _filename); // Logger determines caller

-- or --

Logger.DebugFormat(this, "File not found: {0}", _filename); // Pass in the caller

Im zweiten Beispiel müsste der Logger einen Stack-Trace erstellen, um zu sehen, wer ihn aufgerufen hat, oder der Code muss immer im Aufrufer übergeben werden. Mit dem Logger-per-Class-Stil können Sie dies immer noch tun, aber Sie können es einmal pro Klasse und nicht einmal pro Anruf tun und ein schwerwiegendes Leistungsproblem beseitigen.

53
Jeremy Wiebe

Vorteil für die Verwendung von "Logger pro Datei" in NLog: Sie haben die Möglichkeit, Protokolle nach Namespace und Klassennamen zu verwalten. Beispiel:

<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 bietet dazu ein sehr nützliches Code-Snippet. Das nlogger-Snippet erstellt den folgenden Code:

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

Also nur wenige Tastenanschläge und Sie haben einen Logger pro Klasse. Es verwendet Namespace und Klassennamen als Namen des Loggers. Um Ihrem Klassenlogger einen anderen Namen zu geben, können Sie Folgendes verwenden:

private static NLog.Logger logger = NLog.LogManager.GetLogger("MyLib.MyName");

Und wie @JeremyWiebe sagte, müssen Sie keine Tricks verwenden, um den Namen der Klasse zu erhalten, die versucht, eine Nachricht zu protokollieren (oder ein anderes Ziel) mithilfe von ${logger} im Layout.

14
CoperNick

Ich kann einige Gründe für diese Wahl sehen.

  • Sie werden immer wissen, woher eine bestimmte Protokollanweisung stammt, wenn Sie den Namen des Protokollierers in Ihrem Protokollausgabeformat angeben.
  • Sie können steuern, welche Protokollanweisungen auf einer feinkörnigen Ebene angezeigt werden, indem Sie bestimmte Logger aktivieren oder deaktivieren oder deren Stufe einstellen.
5
Peter Recore

In den meisten Fällen liefert der Name der Klasse einen guten Namen für den Logger. Beim Scannen der Protokolldateien können Sie die Protokollnachricht sehen und direkt einer Codezeile zuordnen.

Ein gutes Beispiel, wo dies nicht der beste Ansatz ist, sind die SQL-Protokolle von Hibernate. Es gibt einen gemeinsam genutzten Logger mit dem Namen "Hibernate.SQL" oder ähnliches, bei dem verschiedene Klassen Raw-SQL in eine einzige Logger-Kategorie schreiben.

3
Eric Hauser

Es gibt auch einen Leistungsvorteil bei NLog. Die meisten Benutzer werden verwenden 

Logger logger = LogManager.GetCurrentClassLogger()

Das Nachschlagen der aktuellen Klasse aus der Stack-Ablaufverfolgung erfordert einige (aber nicht viel) Leistung. 

2
Julian

Aus Sicht der Entwicklung ist es am einfachsten, wenn Sie nicht jedes Mal ein Logger-Objekt erstellen müssen. Wenn Sie dies jedoch nicht tun, sondern dynamisch mithilfe von Reflektionen erstellen, wird die Leistung beeinträchtigt. Um dies zu lösen, können Sie den folgenden Code verwenden, der den Logger dynamisch asynchron erstellt:

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

        }
    }
}
2
as9876

Zwei Gründe fallen sofort ein:

  1. Durch ein separates Protokoll für jede Klasse können alle Protokollnachrichten/Fehler, die sich auf eine bestimmte Klasse beziehen, problemlos gruppiert werden.
  2. Wenn Sie ein Protokoll innerhalb einer Klasse haben, können Sie interne Details protokollieren, auf die außerhalb der Klasse möglicherweise nicht zugegriffen werden kann (z. B. privater Status, Informationen, die sich auf die Implementierung einer Klasse beziehen usw.).
1
Dan Tao

Wahrscheinlich, weil Sie Methoden protokollieren möchten, die nur für die Klasse sichtbar sind, ohne die Kapselung zu beschädigen. Dies macht es auch einfacher, die Klasse in einer anderen Anwendung zu verwenden, ohne die Protokollierungsfunktion zu beeinträchtigen.

0
Rodrick Chapman

Ermöglicht die einfache Konfiguration von Appendern nach Namespace oder Klasse.

0
dotjoe

Wenn Sie NLOG verwenden, können Sie die Callsite in der Konfiguration angeben. Dadurch werden der Klassenname und die Methode aufgezeichnet, an der sich die Protokollierungsanweisung befunden hat.

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

Sie könnten dann eine Konstante für Ihren Loggernamen oder den Assemblynamen verwenden.

Haftungsausschluss: Ich weiß nicht, wie NLOG diese Informationen sammelt. Meine Vermutung wäre eine Reflexion, so dass Sie möglicherweise die Leistung berücksichtigen müssen. Es gibt einige Probleme mit Async-Methoden, wenn Sie NLOG v4.4 oder höher nicht verwenden.

0
user6271979