48 Stimmen

Warum ist es nicht empfehlenswert, LoggerFactory.getLogger(...) jedes Mal aufzurufen?

Ich habe tonnenweise Beiträge und Dokumente (auf dieser Website und anderswo) gelesen, die darauf hinweisen, dass das empfohlene Muster für die SFL4J-Protokollierung lautet:

public class MyClass {
    final static Logger logger = LoggerFactory.getLogger(MyClass.class);

    public void myMethod() {
        //do some stuff
        logger.debug("blah blah blah");
    }
}

Mein Chef zieht es vor, dass wir nur einen Wrapper verwenden, um Log-Aufrufe abzufangen und Boiler-Plate-Code zur Deklaration des Loggers in jeder Klasse zu vermeiden:

public class MyLoggerWrapper {
    public static void debug(Class clazz, String msg){
        LoggerFactory.getLogger(clazz).debug(msg));
    }
}

und sie einfach so zu verwenden:

public class MyClass {

    public void myMethod() {
        //do some stuff
        MyLoggerWrapper.debug(this.getClass(), "blah blah blah");
    }
}

Ich vermute, dass die Instanziierung eines Loggers jedes Mal, wenn wir protokollieren, etwas teuer ist, aber ich konnte kein Dokument finden, das diese Annahme unterstützt. Außerdem sagt er, dass das Framework (LogBack oder Log4J, wir entscheiden noch) die Logger "zwischenspeichern" wird und dass die Server auf jeden Fall weit unter ihrer Kapazität laufen, so dass dies kein Problem darstellt.

Kann mir jemand helfen, mögliche Probleme bei diesem Ansatz aufzuzeigen?

31voto

matt b Punkte 135206

Hier gibt es ein offensichtliches Problem mit diesem Ansatz: Die String-Nachrichten werden bei jedem Aufruf von debug() gibt es keine offensichtliche Möglichkeit, eine Guard-Klausel mit Ihrem Wrapper zu verwenden.

Das Standard-Idiom mit log4j/commons-logging/slf4j ist die Verwendung einer Guard-Klausel wie z.B.:

if (log.isDebugEnabled()) log.debug("blah blah blah");

Mit dem Ziel, dass, wenn die DEBUG für den Logger nicht aktiviert ist, kann der Compiler die Verkettung längerer Strings, die Sie ihm senden, vermeiden:

if (log.isDebugEnabled()) log.debug("the result of method foo is " + bar 
     + ", and the length is " + blah.length());

Siehe "Was ist der schnellste Weg, (nicht) zu protokollieren?" im SLF4J o log4j FAQ.

Ich würde von dem "Wrapper", den Ihr Chef vorschlägt, abraten. Eine Bibliothek wie slf4j oder commons-logging ist bereits eine Fassade um die eigentliche zugrunde liegende Logging-Implementierung. Darüber hinaus wird jeder Aufruf des Loggers viel länger - vergleichen Sie das oben mit

 MyLoggerWrapper.debug(Foo.class, "some message");

Dies ist die Art von trivialem und unwichtigem "Wrapping" und Verschleierung, die keinen wirklichen Zweck erfüllt, außer dem Hinzufügen von indirekten Ebenen und dem Verunstalten Ihres Codes. Ich denke, dass Ihr Chef wichtigere Themen finden kann, über die er sich aufregen kann.

12voto

Péter Török Punkte 111735

Die Logger-Objekte werden sicherlich wiederverwendet, so dass so oder so keine zusätzliche Instanzierung stattfinden wird. Das größere Problem, das ich sehe, ist, dass Ihre Datei/Zeilennummer Info nutzlos sein wird, da der Logger immer treu protokollieren wird, dass jede Nachricht von Klasse ausgegeben wurde LoggerWrapper , Zeile 12 :-(

OTOH SLF4J ist bereits eine Wrapper-Fassade, die das verwendete Logging-Framework verbirgt, so dass Sie frei zwischen verschiedenen Logging-Implementierungen wechseln können. Daher sehe ich absolut keinen Grund, das hinter einem weiteren Wrapper zu verstecken.

12voto

Stephen C Punkte 665668

Wiederholte Anrufe bei LoggerFactory.getLogger(clazz) sollte nicht jedes Mal zu einem neuen Logger-Objekt führen. Das bedeutet jedoch nicht, dass die Aufrufe kostenlos sind. Das tatsächliche Verhalten hängt zwar vom Protokollierungssystem hinter der Fassade ab, aber es ist sehr wahrscheinlich, dass jeder getLogger-Aufruf eine Suche in einer gleichzeitigen oder synchronisierten Datenstruktur nach sich zieht 1 um nach einer bereits vorhandenen Instanz zu suchen.

Wenn Ihre Anwendung viele Aufrufe an Ihre MyLoggerWrapper.debug Methode kann sich dies zu einem erheblichen Leistungsverlust summieren. Und in einer Multithreading-Anwendung kann dies ein Engpass für die Gleichzeitigkeit sein.

Andere Fragen, die in anderen Antworten genannt wurden, sind ebenfalls wichtig:

  • Ihre Anwendung kann nicht mehr die logger.isDebugEnabled() um den Overhead zu minimieren, wenn die Fehlersuche deaktiviert ist.
  • El MyLoggerWrapper Klasse verschleiert die Klassennamen und Zeilennummern der Debug-Aufrufe Ihrer Anwendung.
  • Der Code mit MyLoggerWrapper wird wahrscheinlich ausführlicher sein, wenn Sie mehrere Logger-Aufrufe machen. Und die Ausführlichkeit wird in dem Bereich sein, wo sie die Lesbarkeit am meisten beeinträchtigt, d.h. in den Methoden, die Dinge tun, die protokolliert werden müssen.

Schließlich ist dies einfach "nicht die Art und Weise, wie es gemacht wird".


1 - Anscheinend ist es ein Hashtable in Logback und Log4j, und das bedeutet, dass das Potenzial für einen Gleichzeitigkeitsengpass definitiv vorhanden ist. Beachten Sie, dass dies keine Kritik an diesen Logging-Frameworks ist. Vielmehr ist die getLogger Methode wurde nicht für diese Art der Verwendung konzipiert/optimiert.

10voto

oksayt Punkte 4242

Zu den bereits genannten Gründen kommt hinzu, dass der Vorschlag Ihres Chefs schlecht ist, weil:

  • Es zwingt Sie, jedes Mal, wenn Sie etwas protokollieren wollen, etwas einzugeben, das nichts mit der Protokollierung zu tun hat: this.getClass()
  • Schafft eine uneinheitliche Schnittstelle zwischen statischen und nicht-statischen Kontexten (weil es keine this in einem statischen Kontext)
  • Die zusätzlichen unnötigen Parameter schaffen Raum für Fehler und machen es möglich, dass Anweisungen in derselben Klasse an verschiedene Logger gehen (denken Sie an unvorsichtiges Copy-Paste).
  • Das spart zwar 74 Zeichen der Logger-Deklaration, fügt aber jedem Logging-Aufruf 27 zusätzliche Zeichen hinzu. Das heißt, wenn eine Klasse den Logger mehr als 2 Mal verwendet, erhöht sich die Anzahl der Zeichen für den Standardcode.

7voto

SPee Punkte 598

Wenn Sie etwas wie: MyLoggerWrapper.debug(this.getClass(), "blah") Sie erhalten falsche Klassennamen, wenn Sie AOP-Frameworks oder Code-Injection-Tools verwenden. Die Klassennamen sind nicht wie der Ursprung, sondern ein generierter Klassenname. Und ein weiterer Nachteil der Verwendung des Wrappers: Für jede Protokollanweisung müssen Sie zusätzlichen Code einfügen "MyClass.class" .

El 'Zwischenspeichern' der Logger hängt von den verwendeten Frameworks ab. Aber selbst wenn dies der Fall ist, muss es den gewünschten Logger für jede Log-Aussage, die Sie machen. Wenn Sie also 3 Anweisungen in einer Methode haben, muss sie 3 Mal nachgeschlagen werden. Die Verwendung als static variabel muss es nur einmal nachschlagen!

Und wie schon gesagt: Sie verlieren die Möglichkeit, die if( log.isXXXEnabled() ){} für eine Reihe von Anweisungen.

Was hat Ihr Chef gegen den "von der Gemeinschaft standardmäßig akzeptierten und empfohlenen Weg"? Die Einführung des Wrappers führt nicht zu mehr Effizienz. Stattdessen müssen Sie den Klassennamen für jede Protokollanweisung verwenden. Nach einer Weile wollen Sie das "verbessern", also fügen Sie eine weitere Variable oder einen weiteren Wrapper hinzu und machen es sich damit noch schwerer.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X