Sollte der Logger privat statisch sein oder nicht?

103

Sollte der Logger als statisch deklariert werden oder nicht? Normalerweise habe ich zwei Arten von Deklarationen für einen Logger gesehen:

    protected Log log = neuer Log4JLogger (aClass.class);

oder

    privates statisches Protokoll log = neuer Log4JLogger (aClass.class);

Welches sollte verwendet werden? Was sind die Vor- und Nachteile von beiden?

Drahakar
quelle
1
Die Protokollierung ist ein Querschnittsthema. Verwenden Sie Aspekte und die Frage ist strittig.
Dave Jarvis
4
staticist eine Referenz pro Klasse. Nicht statisch ist eine Referenz pro Instanz (+ Initialisierung). In einigen Fällen hat letzteres erhebliche Auswirkungen auf den Speicher, wenn Sie Tonnen von Instanzen haben. Verwenden Sie das Nicht-Statische niemals in einem häufigen Objekt. Ich benutze immer die statische Version. (was in Großbuchstaben geschrieben werden sollte LOG )
Hat aufgehört - Anony-Mousse
2
Verwenden Sie, wie bereits vorgeschlagen, AOP und Anmerkungen, zum Beispiel: jcabi.com/jcabi-aspects/annotation-loggable.html
yegor256
1
RobertHume die statische Version ist eine Konstante verwendet. Genau deshalb sollte es in Großbuchstaben geschrieben werden.
Hat aufgehört - Anony-Mousse
2
Nein, es sollte private static final Log logKleinbuchstaben sein. Der Logger ist keine Konstante, der Logger ist ein statisches Endobjekt (das mutiert werden kann). Persönlich benutze ich immer logger.
Osundblad

Antworten:

99

Der Vorteil des nicht statischen Formulars besteht darin, dass Sie es in einer (abstrakten) Basisklasse wie folgt deklarieren können, ohne sich Sorgen machen zu müssen, dass der richtige Klassenname verwendet wird:

protected Log log = new Log4JLogger(getClass());

Der Nachteil ist jedoch offensichtlich, dass für jede Instanz der Klasse eine ganz neue Logger-Instanz erstellt wird. Dies ist zwar an sich nicht teuer, verursacht jedoch einen erheblichen Overhead. Wenn Sie dies vermeiden möchten, möchten Sie staticstattdessen das Formular verwenden. Der Nachteil ist jedoch, dass Sie es in jeder einzelnen Klasse deklarieren und in jeder Klasse darauf achten müssen, dass der richtige Klassenname während der Erstellung des Loggers verwendet wird, da er getClass()nicht im statischen Kontext verwendet werden kann. In der durchschnittlichen IDE können Sie jedoch eine Vorlage für die automatische Vervollständigung erstellen. ZB logger+ ctrl+space.

Wenn Sie den Logger hingegen von einer Factory beziehen, die wiederum die bereits instanziierten Logger zwischenspeichern kann, führt die Verwendung des nicht statischen Formulars nicht zu einem so hohen Overhead. Log4j hat zum Beispiel eine LogManagerfür diesen Zweck.

protected Log log = LogManager.getLogger(getClass());
BalusC
quelle
6
abstract Log getLogger();In der abstrakten Klasse deklarieren . Implementieren Sie diese Methode und geben Sie den statischen Logger für die jeweilige Instanz zurück. Fügen Sie private final static Log LOG = LogManager.getLogger(Clazz.class);Ihrer IDE-Klassenvorlage hinzu.
Hat aufgehört - Anony-Mousse
2
Für slf4j:protected Logger log = LoggerFactory.getLogger(getClass());
Markus Pscheidt
3
@BalusC Das Problem beim Übergeben von getClass () an die getLogger-Methode besteht darin, dass die Klasse der aktuellen Instanz zurückgegeben wird. Normalerweise ist es wünschenswerter, dass die Protokollierung der Klasse zugeordnet wird, in der sich der Code befindet. Wenn sich der Protokollierungscode beispielsweise in der Klasse Parent befindet, möchten wir, dass die Protokollierung Parent zugeordnet wird, obwohl die ausführende Instanz eine Instanz der Klasse Child ist, die eine Unterklasse von Parent ist. Mit getClass () wird es fälschlicherweise mit dem Kind
verknüpft
@inor: "falsch"? Wenn Sie die Klasse nicht abstrahieren möchten, sollten Sie die geerbte getClass () erst gar nicht verwenden. Es gibt Entwickler, die es für richtig und nützlich halten, da es Informationen anzeigt, in welcher Unterklasse genau die Logik ausgeführt wurde.
BalusC
2
@ BalusC getLogger (getClass ()) führt dazu, dass der Name der Unterklasse immer falsch protokolliert wird. Protokollierungsklassen sollten immer getLogger (Clazz.class) ausführen, um die vom Code in der Klasse Clazz vorgenommene Protokollierung zuzuordnen. Entwickler, die wissen möchten, welche der Unterklassen ausgeführt wird (z. B. SubClazz erweitert Clazz), sollten dies in SubClazz tun: getLogger (SubClazz.class) und so etwas wie: log.info ("Aufruf von <etwas in meiner Basisklasse>");
Inor
44

Früher dachte ich, dass alle Logger statisch sein sollten. aber dieser Artikel bei wiki.apache.org jedoch einige wichtige Speicherprobleme in Bezug auf Classloader-Lecks . Wenn Sie einen Logger als statisch deklarieren, wird verhindert, dass die deklarierende Klasse (und die zugehörigen Klassenladeprogramme) in J2EE-Containern, die einen gemeinsam genutzten Klassenladeprogramm verwenden, als Müll gesammelt werden. Dies führt zu PermGen-Fehlern, wenn Sie Ihre Anwendung genügend oft erneut bereitstellen.

Ich sehe keine Möglichkeit, dieses Problem mit dem Classloader-Leck zu umgehen, außer Logger als nicht statisch zu deklarieren.

Piepera
quelle
4
Ich vermutete, dass das statische Feld ebenfalls ein Problem mit dem Speicherverlust haben würde. Nicht statisch kann Leistungsprobleme haben, wie andere sagten. Was ist dann der ideale Weg?
Liang
@piepera Das Hauptproblem, das in dem Artikel beschrieben wird, auf den Sie verwiesen haben, ist die Möglichkeit, die Protokollierungsstufe in jeder Anwendung zu steuern, wenn "der Fall berücksichtigt wird, wenn eine Klasse mit" private static Log log = "über einen ClassLoader bereitgestellt wird, der angeblich mehrere Vorfahren hat unabhängige "Anwendungen". Ich sehe das nicht als Problem, da in dieser speziellen Situation die Anwendungen eine "gemeinsame Basis" haben und in dieser "gemeinsamen Basis" die Protokollierungsstufe für die Klasse festgelegt ist und ja, dies gilt für alle Anwendungen ... aber behalten Sie Denken Sie daran, dass diese Klasse außerhalb dieser Anwendungen [geladen] ist
oder
17

Der wichtigste Unterschied besteht darin, wie sich dies auf Ihre Protokolldateien auswirkt: In welche Kategorie fallen Protokolle?

  • Bei Ihrer ersten Wahl landen die Protokolle einer Unterklasse in der Kategorie der Oberklasse. Das scheint mir sehr kontraintuitiv zu sein.
  • Es gibt eine Variante Ihres ersten Falls:

    protected Log log = neuer Log4JLogger (getClass ());

    In diesem Fall gibt Ihre Protokollkategorie an, an welchem ​​Objekt der protokollierte Code gearbeitet hat.

  • Bei Ihrer zweiten Wahl (private statische) ist die Protokollkategorie die Klasse, die den Protokollierungscode enthält. Normalerweise also die Klasse, die das tut, was protokolliert wird.

Ich würde diese letzte Option wärmstens empfehlen. Es hat diese Vorteile im Vergleich zu den anderen Lösungen:

  • Es besteht eine direkte Beziehung zwischen dem Protokoll und dem Code. Es ist leicht herauszufinden, woher eine Protokollnachricht stammt.
  • Wenn jemand die Protokollierungsstufen anpassen muss (was pro Kategorie erfolgt), liegt dies normalerweise daran, dass er an bestimmten Nachrichten interessiert ist (oder nicht), die von einer bestimmten Klasse geschrieben wurden. Wenn die Kategorie nicht die Klasse ist, die die Nachrichten schreibt, ist es schwieriger, die Ebenen zu optimieren.
  • Sie können sich bei statischen Methoden anmelden
  • Logger müssen nur einmal pro Klasse initialisiert (oder nachgeschlagen) werden, also beim Start, anstatt für jede erstellte Instanz.

Es hat auch Nachteile:

  • Es muss in jeder Klasse deklariert werden, in der Sie Nachrichten protokollieren (keine Wiederverwendung von Protokollierern der Oberklasse).
  • Sie müssen darauf achten, dass Sie beim Initialisieren des Loggers den richtigen Klassennamen eingeben. (Aber gute IDEs erledigen das für Sie).
Wouter Coekaerts
quelle
4

Verwenden Sie die Umkehrung der Steuerung und übergeben Sie den Logger an den Konstruktor. Wenn Sie den Logger innerhalb der Klasse erstellen, werden Sie mit Ihren Unit-Tests eine teuflische Zeit haben. Sie schreiben Unit-Tests, nicht wahr?

Wayne Allen
quelle
5
Unit-Tests, die die von Ihnen erzeugte Protokollierung untersuchen, klingen sowohl nutzlos als auch unglaublich spröde.
Michael
1
Nützlichkeit je nach zu testendem System. Manchmal ist die Protokollierung alles, worauf Sie Zugriff haben.
Wayne Allen
@ Wayne Allen Wenn Sie Unit-Tests durchführen, haben Sie per Definition auch Testergebnisse. Schlagen Sie eine Situation vor, in der ein Unit-Test durchgeführt wird, aber keine Testergebnisse vorliegen? nur die Protokolle haben?
Inor
Das Erstellen des Loggers innerhalb der Klasse verursacht keine Probleme. Können Sie ein einfaches Beispiel zeigen, das schwer zu UT ist, weil die Klasse einen eigenen Logger erstellt?
Inor
1
Sicher. Wie wäre es mit einem Logger, der E-Mails sendet. Das möchten Sie nicht jedes Mal tun, wenn Sie Ihre Tests ausführen. Und wie behaupten Sie die Nebenwirkung?
Wayne Allen