Ich habe mich für das Log4J-Protokollierungsframework für ein neues Java-Projekt entschieden. Ich frage mich, welche Strategie ich zum Erstellen / Verwalten von Logger-Instanzen verwenden soll und warum.
eine Instanz von Logger pro Klasse, z
class Foo { private static final Logger log = Logger.getLogger(Foo.class); }
- eine Instanz von Logger pro Thread
- eine Instanz von Logger pro Anwendung
- Horizontales Slicing: Eine Instanz von Logger in jeder Ebene einer Anwendung (z. B. die Ansichtsebene, die Controller-Ebene und die Persistenz-Ebene).
- Vertikales Slicing: Eine Instanz von Logger in funktionalen Partitionen der Anwendung
Hinweis: Dieses Problem wird in diesen Artikeln bereits teilweise berücksichtigt:
Antworten:
Normalerweise werden Logger pro Klasse eingerichtet, da dies eine nette logische Komponente ist. Threads sind bereits Teil der Protokollnachrichten (sofern Ihr Filter sie anzeigt), sodass das Aufteilen von Protokollierern auf diese Weise wahrscheinlich redundant ist.
In Bezug auf anwendungs- oder schichtbasierte Logger besteht das Problem darin, dass Sie einen Platz finden müssen, an dem Sie das Logger-Objekt anbringen können. Keine wirklich große Sache. Das größere Problem ist, dass einige Klassen auf mehreren Ebenen aus mehreren Anwendungen verwendet werden können. Es kann schwierig sein, Ihren Logger richtig zu machen. Oder zumindest knifflig.
... und das Letzte, was Sie wollen, sind schlechte Annahmen in Ihrem Protokollierungssetup.
Wenn Sie sich für Anwendungen und Ebenen interessieren und einfache Trennstellen haben, ist der NDC der richtige Weg. Der Code kann manchmal etwas übertrieben sein, aber ich weiß nicht, wie oft ich von einem genauen Kontextstapel gespeichert wurde, der mir zeigt, dass Foo.bar () von Anwendung X in Schicht Y aufgerufen wurde.
quelle
Die am häufigsten verwendete Strategie besteht darin, einen Logger pro Klasse zu erstellen. Wenn Sie neue Threads erstellen, geben Sie ihnen einen nützlichen Namen, damit ihre Protokollierung leicht unterschieden werden kann.
Das Erstellen von Protokollierern pro Klasse bietet den Vorteil, dass Sie die Protokollierung in der Paketstruktur Ihrer Klassen ein- und ausschalten können:
Das oben Gesagte würde den gesamten Apache-Bibliothekscode auf
INFO
Level setzen und die Protokollierung von Ihrem eigenen Code aufDEBUG
Level umstellen, mit Ausnahme des ausführlichen Pakets.quelle
private static Logger logger = Logger.getLogger(MyClass.class);
in jede Klasse zu kopieren ?Logger
Konstruktor verwenden Sie, um einenLogger
für ein Paket zu erstellen ? Ist dergetLogger(String name)
Name nur ein String, der das Paket angibt (dh "abc")? Wenn Sie dies tun, können Sie die Protokollierungsstufe so konfigurieren, wie Sie a la erwähnenlog4j.logger.a.b.c = INFO
?private static Logger logger = Logger.getLogger(MyClass.class.getPackage());
Die Logger-Hierarchie funktioniert auch, wenn Sie Logger mit den Namen "ab", "abc" usw. erstellen. Siehe das Handbuch unter logging.apache.org/log4j/1.2/manual.htmlIch bin mir sicher, dass dies keine bewährte Methode ist, aber ich habe zuvor einige Startzeiten für Anwendungen festgelegt, um Codezeilen zu speichern. Insbesondere beim Einfügen von:
... Entwickler vergessen oft, "MyClass" in den aktuellen Klassennamen zu ändern, und mehrere Logger zeigen immer auf die falsche Stelle. Das ist schlecht.
Ich habe gelegentlich geschrieben:
static Logger logger = LogUtil.getInstance();
Und:
class LogUtil { public Logger getInstance() { String callingClassName = Thread.currentThread().getStackTrace()[2].getClass().getCanonicalName(); return Logger.getLogger(callingClassName); } }
Die "2" in diesem Code könnte falsch sein, aber das Wesentliche ist da; Nehmen Sie einen Leistungstreffer, um (beim Laden der Klasse als statische Variable) den Klassennamen zu finden, damit ein Entwickler nicht wirklich die Möglichkeit hat, dies falsch einzugeben oder einen Fehler einzuführen.
Ich bin im Allgemeinen nicht begeistert von Leistungseinbußen, um Entwicklerfehler zur Laufzeit zu vermeiden, aber wenn es einmal als Singleton passiert? Klingt für mich oft nach einem guten Handel.
quelle
Wie von anderen gesagt, würde ich einen Logger pro Klasse erstellen:
private final static Logger LOGGER = Logger.getLogger(Foo.class);
oder
private final Logger logger = Logger.getLogger(this.getClass());
Ich habe es jedoch in der Vergangenheit als nützlich empfunden, andere Informationen im Logger zu haben. Wenn Sie beispielsweise eine Website haben, können Sie die Benutzer-ID in jede Protokollnachricht aufnehmen. Auf diese Weise können Sie alles verfolgen, was ein Benutzer tut (sehr nützlich zum Debuggen von Problemen usw.).
Der einfachste Weg, dies zu tun, ist die Verwendung eines MDC. Sie können jedoch einen Logger verwenden, der für jede Instanz der Klasse mit dem Namen einschließlich der Benutzer-ID erstellt wurde.
Ein weiterer Vorteil der Verwendung eines MDC besteht darin, dass Sie bei Verwendung von SL4J die Einstellungen abhängig von den Werten in Ihrem MDC ändern können. Wenn Sie also alle Aktivitäten für einen bestimmten Benutzer auf DEBUG-Ebene protokollieren und alle anderen Benutzer auf ERROR belassen möchten, können Sie dies tun. Sie können je nach MDC auch unterschiedliche Ausgaben an unterschiedliche Stellen umleiten.
Einige nützliche Links:
http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/MDC.html
http://www.slf4j.org/api/index.html?org/slf4j/MDC.html
quelle
this.getClass()
Teil. Auf diese Weise treten keine Kopier- / Einfügefehler bezüglich des Loggernamens auf.private static final Log log = LogFactory.getLog(MyClass.class);
Beispiele für Vorlagen:
private static final Log log = LogFactory.getLog($class$.class); // live template 'log' if (log.isDebugEnabled()) log.debug(String.format("$string$", $vars$)); // live template 'ld', 'lw', 'le' ...
quelle
Eine weitere Option: Sie können versuchen, AspectJ beim Protokollieren zu schneiden. Überprüfen Sie hier: Vereinfachen Sie Ihre Protokollierung . (Wenn Sie AOP nicht verwenden möchten , können Sie slf4j suchen. )
//Without AOP Class A{ methodx(){ logger.info("INFO"); } } Class B{ methody(){ logger.info("INFO"); } } //With AOP Class A{ methodx(){ ...... } } Class B{ methody(){ ...... } } Class LoggingInterceptor{ //Catched defined method before process public void before(...xyz){ logger.info("INFO" + ...xyz); } //Catched defined method after processed public void after(...xyz){ logger.info("INFO" + ...xyz); } ..... }
PS: AOP wird besser sein, es ist trocken (nicht wiederholen) .
quelle
Die beste und einfachste Methode zum Erstellen benutzerdefinierter Protokollierer, die nicht mit einem Klassennamen verknüpft sind, ist:
// create logger Logger customLogger = Logger.getLogger("myCustomLogName"); // create log file, where messages will be sent, // you can also use console appender FileAppender fileAppender = new FileAppender(new PatternLayout(), "/home/user/some.log"); // sometimes you can call this if you reuse this logger // to avoid useless traces customLogger.removeAllAppenders(); // tell to logger where to write customLogger.addAppender(fileAppender); // send message (of type :: info, you can use also error, warn, etc) customLogger.info("Hello! message from custom logger");
Wenn Sie jetzt einen anderen Logger in derselben Klasse benötigen, kein Problem :) Erstellen Sie einfach einen neuen
// create logger Logger otherCustomLogger = Logger.getLogger("myOtherCustomLogName");
Sehen Sie sich jetzt den obigen Code an und erstellen Sie einen neuen Dateiordner, damit Ihre Ausgabe in einer anderen Datei gesendet wird
Dies ist nützlich für (mindestens) 2 Situationen
wenn Sie einen separaten Fehler von info und warnen möchten
Wenn Sie mehrere Prozesse verwalten und von jedem Prozess eine Ausgabe benötigen
ps. habe Fragen ? Fragen Sie nur! :) :)
quelle
Die übliche Konvention ist "eine logger pr-Klasse und verwenden Sie den Klassennamen als Namen". Das ist ein guter Rat.
Meine persönliche Erfahrung ist, dass diese Logger-Variable NICHT als statisch deklariert werden sollte, sondern als Instanzvariable, die für jede neue abgerufen wird. Auf diese Weise kann das Protokollierungsframework zwei Aufrufe je nach Herkunft unterschiedlich behandeln. Eine statische Variable ist für ALLE Instanzen dieser Klasse (in diesem Klassenladeprogramm) identisch.
Außerdem sollten Sie alle Möglichkeiten mit dem Protokollierungs-Backend Ihrer Wahl kennenlernen. Möglicherweise haben Sie Möglichkeiten, die Sie nicht für möglich gehalten haben.
quelle
Wenn Sie mehrere EARs / WARs bereitstellen, ist es möglicherweise besser, die Datei log4j.jar weiter oben in der Classloader-Hierarchie zu verpacken.
dh nicht in WAR oder EAR, sondern im System-Classloader Ihres Containers, andernfalls schreiben mehrere Log4J-Instanzen gleichzeitig in dieselbe Datei, was zu seltsamem Verhalten führt.
quelle
Wenn Ihre Anwendung den SOA-Prinzipien folgt, haben Sie für jeden Dienst A die folgenden Komponenten:
So wird es einfacher, ein aController.log aService.log aExecutor.log und ein aPersistance.log zu haben
Dies ist eine schichtbasierte Trennung, sodass alle Ihre Remoting / REST / SOAP-Klassen in das aController.log schreiben
Alle Ihre Planungsmechanismen, Backend-Dienste usw. werden in aService.log geschrieben
Alle Aufgabenausführungen werden in aExecutor.log usw. geschrieben.
Wenn Sie einen Multithread-Executor haben, müssen Sie möglicherweise einen Protokollspeicher oder eine andere Technik verwenden, um Protokollnachrichten für mehrere Threads richtig auszurichten.
Auf diese Weise haben Sie immer 4 Protokolldateien, was nicht viel und nicht zu wenig ist. Ich sage Ihnen aus Erfahrung, dass dies das Leben wirklich einfacher macht.
quelle