Ist Logger.getLogger (MyClass.class) der beste Weg, um log4j-Logger zu initialisieren?


13

In diesem Mkyong-Tutorial wird vorgeschlagen, Logger folgendermaßen zu initialisieren:

@Controller
public class WelcomeController {

    private static final Logger logger = Logger.getLogger(WelcomeController.class);

   // etc

}

Vermutlich initialisiert vermutlich jede andere Klasse, die einen Logger hat, ihre Logger auf die gleiche Weise.

Meine Frage ist - ist das der beste Weg, es zu tun? Es scheint ... sich zu wiederholen.


2
Was finden Sie ausführlich darüber (abgesehen von Javas Syntax)? Sie müssen eine Variable erstellen, um den Logger zu halten, und Sie müssen getLogger()den Namen des Loggers angeben, der abgerufen werden soll.
kdgregory

2
@kdgregory die Tatsache, dass ich in jeder Klasse das Gleiche mache.
Dwjohnston

1
Werfen

3
Zu alt für eine Migration, aber diese Frage ist hier nicht zum Thema und besser für StackOverflow geeignet.
Andres F.

1
@AndresF. Ich denke, ich habe es hier eingefügt, weil es eher eine Frage des Codestils / der Entwurfsmuster als ein technisches Problem ist.
Dwjohnston

Antworten:


16

Ihr Kommentar besagt, dass "ausführlich" sich auf die Notwendigkeit bezieht, diese Codezeile in jeder Klasse zu wiederholen. Meine erste Antwort ist, dass das Hinzufügen von zwei Codezeilen (Variablendefinition plus Importanweisung) zu jeder Klasse im Großen und Ganzen keine so große Sache ist. Zumal Sie sie nur zu den Klassen hinzufügen müssen, die Verhalten haben und daher die Protokollierung durchführen müssen. Die von Ihnen verwendete Codezeile ist jedoch anfällig für Fehler beim Kopieren und Einfügen (dazu später mehr).

Da Sie jedoch Alternativen wünschen, sind hier einige aufgeführt, mit Gründen, aus denen Sie diese möglicherweise verwenden möchten oder nicht.

Verwenden Sie einen einzelnen Logger für die gesamte App

Wenn Sie sich nicht darum kümmern, welche Klasse Bericht erstattet, oder bereit sind, den gesamten erforderlichen Kontext in die Nachricht einzufügen, erledigt ein einfacher Singleton-Logger die Aufgabe:

LoggerSingleton.getInstance().debug("MyController is running")

Meiner Meinung nach besteht einer der großen Vorteile eines Protokollierungsframeworks darin, dass der Kontext von separaten Protokollierungsinstanzen bereitgestellt wird - schon allein, um die Protokollnachrichten an verschiedene Ziele zu leiten. Ich würde das nicht aufgeben, nur um eine Codezeile zu speichern (Sie benötigen noch den Import).

Außerdem erhöht dies die Ausführlichkeit am Verwendungsort, was zu weitaus mehr Tastenanschlägen führt.

Erstellen Sie Ihre Logger am Verwendungsort

Ich werfe dieses raus, nur weil es die Variable eliminiert. Ich glaube nicht, dass ich das kommentieren muss. Obwohl es meine bevorzugte Technik zum Abrufen einer Logger-Instanz zeigt.

Logger.getLogger(getClass()).debug("blah blah blah");

Verwenden Sie einen Bean-Postprozessor, um den Logger zu injizieren

In Ihrem Beispiel wird Spring verwendet, und mit Spring können Sie sich in den Bean-Initialisierungscode einbinden. Sie können einen Postprozessor erstellen, der die Bean auf eine loggerMitgliedsvariable überprüft und eine LoggerInstanz erstellt, wenn sie eine findet.

Während ein solcher Postprozessor nur aus ein paar Dutzend Codezeilen besteht, ist er ein weiterer bewegender Teil Ihrer Anwendung und daher eine weitere potenzielle Fehlerquelle. Ich bevorzuge es, so wenig wie möglich davon zu haben.

Verwenden Sie ein Mixin

Scala und Groovy bieten Eigenschaften , mit denen Sie das Verhalten zusammenfassen können. Ein typisches Scala-Muster besteht darin, ein LoggingMerkmal zu erstellen und es dann der Klasse hinzuzufügen, die protokolliert werden muss:

class MyController with Logging

Dies bedeutet leider, dass Sie die Sprache wechseln müssen. Sofern Sie nicht Java 8 verwenden, können Sie in diesem Fall eine LoggingSchnittstelle mit einer "Standardmethode" erstellen :

public interface Logging {
    default Logger getLogger() {
        return Logger.getLogger(getClass());
    } 
}

Jetzt können Sie innerhalb Ihres Klassencodes einfach verwenden

getLogger().debug("blah blah blah");

Dies ist zwar einfach, hat jedoch einige Nachteile. Zum einen verschmutzt es die Schnittstelle jeder Klasse, die es verwendet, da alle Schnittstellenmethoden öffentlich sind. Vielleicht nicht so schlimm, wenn Sie es nur für Klassen verwenden, die von Spring instanziiert und injiziert werden, insbesondere wenn Sie die Trennung von Schnittstelle und Implementierung befolgen.

Das größere Problem ist, dass bei jedem Aufruf die tatsächliche Logger-Instanz nachgeschlagen werden muss. Welches ist schnell, aber unnötig.

Und Sie benötigen noch eine Importanweisung.

Verschieben Sie den Logger in eine Oberklasse

Ich wiederhole: Ich finde wiederholte Logger-Definitionen nicht ausführlich, aber wenn Sie dies tun, halte ich dies für den besten Ansatz, um sie zu beseitigen.

public abstract class AbstractController {
    protected Logger logger = Logger.getLogger(getClass());
}

Jetzt erben Ihre Controller-Klassen von AbstractControllerund haben Zugriff auf die loggerVariable. Denken Sie daran, dass Sie die @ControllerAnmerkung in die konkrete Klasse einfügen müssen.

Einige Leute werden dies als eine Perversion der Vererbung empfinden. Ich habe versucht, sie zu beruhigen, indem ich die Klasse benannte AbstractControllerund nicht AbstractProjectClass. Sie können selbst entscheiden, ob es eine Beziehung gibt oder nicht .

Andere Personen lehnen die Verwendung einer Instanzvariablen anstelle einer statischen Variablen ab. Statische IMO- Protokollierer sind anfällig für Fehler beim Kopieren und Einfügen, da Sie explizit auf den Klassennamen verweisen müssen. getClass()stellt sicher, dass Ihr Logger immer korrekt ist.


Ich denke, Sie sollten vorsichtig mit getClass () in der Vererbungsklasse MethodHandles.lookup () sein. LookupClass () ist besser nach jdk 7
yuxh

@luxh - hast du eine Erklärung dafür?
kdgregory

Überprüfen Sie hier: stackoverflow.com/a/6653577/4652536
yuxh

@yuxh - Die einzige Erwähnung von MethodHandles in dieser Frage (die nicht in der von Ihnen verlinkten Antwort enthalten war) ist eine ähnliche, nicht unterstützte Behauptung mit einem Kommentar, der um Klarstellung bittet. Haben Sie maßgebliche Unterstützung für die Behauptung, MethodHandles.lookup().lookupClass()die besser ist als Object.getClass()?
kdgregory

MethodHandles.lookup().lookupClass()kann für statische Variablen verwendet werden, ist nicht anfällig für Fehler beim Kopieren und Einfügen und ist schnell: stackoverflow.com/a/47112323/898747 Es bedeutet zwar einen zusätzlichen Inport, aber ich mag es sehr, wenn meine Logger statisch sind, also ist es zumindest einen Wert wert Erwähnung :)
FableBlaze

0

Um die von @kdgregory bereitgestellte Antwort zu erweitern, stellt Groovy @Slf4j( groovy.util.logging.Slf4j) sofort als Annotation bereit , die eine AST-Transformation für die Klasse ausführt, um sie mit einem Logger zu versehen, der logstandardmäßig den Variablennamen annimmt, wenn er nicht angegeben wird.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.