Zum ersten Mal in meinem Leben befinde ich mich in einer Position, in der ich eine Java-API schreibe, die Open Source ist. Hoffentlich in vielen anderen Projekten enthalten sein.
Für die Protokollierung habe ich (und in der Tat die Leute, mit denen ich arbeite) immer JUL (java.util.logging) verwendet und hatte nie Probleme damit. Jetzt muss ich jedoch genauer verstehen, was ich für meine API-Entwicklung tun soll. Ich habe einige Nachforschungen angestellt und mit den Informationen, die ich habe, werde ich nur verwirrter. Daher dieser Beitrag.
Da ich von JUL komme, bin ich voreingenommen. Mein Wissen über den Rest ist nicht so groß.
Aufgrund meiner Recherchen habe ich folgende Gründe gefunden, warum die Leute JUL nicht mögen:
„Ich begann in Java zu entwickeln , lange bevor Sun Juli veröffentlicht und es war einfach leichter für mich , mit Logging-Framework-X fortzusetzen , anstatt etwas Neues zu lernen“ . Hmm. Ich mache keine Witze, das sagen die Leute. Mit diesem Argument könnten wir alle COBOL machen. (Ich kann mich jedoch durchaus darauf beziehen, dass ich selbst ein fauler Typ bin.)
"Ich mag die Namen der Protokollierungsstufen in JUL nicht" . Ok, im Ernst, das ist einfach kein Grund genug, eine neue Abhängigkeit einzuführen.
"Ich mag das Standardformat der Ausgabe von JUL nicht" . Hmm. Dies ist nur eine Konfiguration. Sie müssen nicht einmal Code tun. (Richtig, früher mussten Sie möglicherweise Ihre eigene Formatter-Klasse erstellen, um es richtig zu machen).
„Ich benutze andere Bibliotheken , die auch Logging-Framework-X verwenden , so dass ich dachte , es einfacher , nur dass man verwenden“ . Dies ist ein zirkuläres Argument, nicht wahr? Warum verwendet "jeder" Logging-Framework-X und nicht JUL?
"Alle anderen verwenden Logging-Framework-X" . Dies ist für mich nur ein Sonderfall der oben genannten. Die Mehrheit ist nicht immer richtig.
Die wirklich große Frage ist also, warum nicht JUL? . Was habe ich vermisst? Das Grundprinzip für die Protokollierung von Fassaden (SLF4J, JCL) ist, dass es in der Vergangenheit mehrere Protokollierungsimplementierungen gegeben hat und der Grund dafür aus meiner Sicht wirklich auf die Zeit vor JUL zurückgeht. Wenn JUL perfekt wäre, gäbe es keine Holzfassaden, oder was? Um die Sache noch verwirrender zu machen, ist JUL bis zu einem gewissen Grad eine Fassade, mit der Handler, Formatierer und sogar der LogManager ausgetauscht werden können.
Sollten wir uns nicht fragen, warum sie überhaupt notwendig waren, anstatt mehrere Methoden zu nutzen, um dasselbe zu tun (Protokollierung)? (und sehen, ob diese Gründe noch existieren)
Ok, meine bisherigen Forschungen haben zu ein paar Dingen geführt, von denen ich sehe, dass sie echte Probleme mit JUL darstellen können:
Leistung . Einige sagen, dass die Leistung in SLF4J den anderen überlegen ist. Dies scheint mir ein Fall vorzeitiger Optimierung zu sein. Wenn Sie Hunderte von Megabyte pro Sekunde protokollieren müssen, bin ich mir nicht sicher, ob Sie auf dem richtigen Weg sind. JUL hat sich ebenfalls weiterentwickelt und die Tests, die Sie unter Java 1.4 durchgeführt haben, sind möglicherweise nicht mehr wahr. Sie können hier darüber lesen , und dieses Update hat es in Java 7 geschafft. Viele sprechen auch über den Overhead der Verkettung von Zeichenfolgen bei Protokollierungsmethoden. Die vorlagenbasierte Protokollierung vermeidet diese Kosten und ist auch in JUL vorhanden. Persönlich schreibe ich nie wirklich vorlagenbasierte Protokollierung. Dafür zu faul. Zum Beispiel, wenn ich das mit JUL mache:
log.finest("Lookup request from username=" + username + ", valueX=" + valueX + ", valueY=" + valueY));
Meine IDE warnt mich und bittet um Erlaubnis, dass sie geändert werden soll in:
log.log(Level.FINEST, "Lookup request from username={0}, valueX={1}, valueY={2}", new Object[]{username, valueX, valueY});
.. was ich natürlich akzeptieren werde. Erlaubnis erteilt ! Danke für deine Hilfe.
Ich schreibe solche Aussagen also nicht selbst, das wird von der IDE gemacht.
Zusammenfassend zum Thema Leistung habe ich nichts gefunden, was darauf hindeuten würde, dass die Leistung von JUL im Vergleich zur Konkurrenz nicht in Ordnung ist.
Konfiguration aus dem Klassenpfad . Out-of-the-Box JUL kann keine Konfigurationsdatei aus dem Klassenpfad laden. Es sind ein paar Codezeilen , um dies zu erreichen. Ich kann sehen, warum dies ärgerlich sein mag, aber die Lösung ist kurz und einfach.
Verfügbarkeit von Ausgabe-Handlern . JUL wird mit 5 sofort einsatzbereiten Ausgabehandlern geliefert: Konsole, Dateistream, Socket und Speicher. Diese können erweitert oder neue geschrieben werden. Dies kann beispielsweise das Schreiben in UNIX / Linux Syslog und Windows Event Log sein. Ich persönlich hatte diese Anforderung noch nie und habe sie auch noch nie gesehen, aber ich kann mich durchaus darauf beziehen, warum sie möglicherweise eine nützliche Funktion ist. Logback wird beispielsweise mit einem Appender für Syslog geliefert. Trotzdem würde ich das argumentieren
- 99,5% des Bedarfs an Ausgabezielen werden durch den sofort einsatzbereiten JUL gedeckt.
- Spezielle Bedürfnisse könnten von benutzerdefinierten Handlern zusätzlich zu JUL und nicht zu etwas anderem berücksichtigt werden. Nichts deutet darauf hin, dass das Schreiben eines Syslog-Ausgabehandlers für JUL länger dauert als für ein anderes Protokollierungsframework.
Ich bin wirklich besorgt, dass ich etwas übersehen habe. Die Verwendung von Protokollierungsfassaden und anderen Protokollierungsimplementierungen als JUL ist so weit verbreitet, dass ich zu dem Schluss kommen muss, dass ich es einfach nicht verstehe. Das wäre leider nicht das erste Mal. :-)
Was soll ich mit meiner API tun? Ich möchte, dass es erfolgreich wird. Ich kann natürlich einfach "mit dem Fluss gehen" und SLF4J implementieren (was heutzutage am beliebtesten zu sein scheint), aber um meinetwillen muss ich immer noch genau verstehen, was mit dem heutigen JUL falsch ist, der all den Flaum rechtfertigt? Werde ich mich selbst sabotieren, indem ich JUL für meine Bibliothek wähle?
Leistung testen
(Abschnitt hinzugefügt von nolan600 am 07-JUL-2012)
Im Folgenden wird von Ceki darauf hingewiesen, dass die Parametrisierung von SLF4J zehnmal oder schneller als die von JUL ist. Also habe ich angefangen, einige einfache Tests durchzuführen. Auf den ersten Blick ist die Behauptung sicherlich richtig. Hier sind die vorläufigen Ergebnisse (aber lesen Sie weiter!):
- Ausführungszeit SLF4J, Backend Logback: 1515
- Ausführungszeit SLF4J, Backend JUL: 12938
- Ausführungszeit JUL: 16911
Die obigen Zahlen sind ms, also ist weniger besser. Der 10-fache Leistungsunterschied ist also zunächst ziemlich eng. Meine erste Reaktion: Das ist viel!
Hier ist der Kern des Tests. Wie zu sehen ist, werden eine Ganzzahl und eine Zeichenfolge in einer Schleife zusammengefasst, die dann in der Protokollanweisung verwendet wird:
for (int i = 0; i < noOfExecutions; i++) {
for (char x=32; x<88; x++) {
String someString = Character.toString(x);
// here we log
}
}
(Ich wollte, dass die Protokollanweisung sowohl einen primitiven Datentyp (in diesem Fall ein int) als auch einen komplexeren Datentyp (in diesem Fall einen String) hat. Ich bin mir nicht sicher, ob es wichtig ist, aber da haben Sie es.)
Die Protokollanweisung für SLF4J:
logger.info("Logging {} and {} ", i, someString);
Die Protokollanweisung für JUL:
logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});
Die JVM wurde mit demselben Test "aufgewärmt", der einmal ausgeführt wurde, bevor die eigentliche Messung durchgeführt wurde. Java 1.7.03 wurde unter Windows 7 verwendet. Die neuesten Versionen von SLF4J (v1.6.6) und Logback (v1.0.6) wurden verwendet. Stdout und stderr wurden auf das Nullgerät umgeleitet.
Es stellt sich jedoch heraus, dass JUL die meiste Zeit damit verbringt, getSourceClassName()
da JUL standardmäßig den Namen der Quellklasse in der Ausgabe druckt, während Logback dies nicht tut. Also vergleichen wir Äpfel und Orangen. Ich muss den Test erneut durchführen und die Protokollierungsimplementierungen auf ähnliche Weise konfigurieren, damit sie tatsächlich dasselbe Material ausgeben. Ich vermute jedoch, dass SLF4J + Logback immer noch die Nase vorn hat, aber weit von den oben angegebenen Anfangszahlen entfernt ist. Bleib dran.
Übrigens: Der Test war das erste Mal, dass ich tatsächlich mit SLF4J oder Logback gearbeitet habe. Eine angenehme Erfahrung. JUL ist sicherlich viel weniger einladend, wenn Sie anfangen.
Testleistung (Teil 2)
(Abschnitt hinzugefügt von nolan600 am 08-JUL-2012)
Wie sich herausstellt, spielt es für die Leistung keine Rolle, wie Sie Ihr Muster in JUL konfigurieren, dh ob es den Quellennamen enthält oder nicht. Ich habe es mit einem sehr einfachen Muster versucht:
java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"
und das änderte die obigen Zeiten überhaupt nicht. Mein Profiler stellte fest, dass der Logger immer noch viel Zeit mit Anrufen verbrachte, getSourceClassName()
auch wenn dies nicht Teil meines Musters war. Das Muster spielt keine Rolle.
Ich komme daher zu dem Thema Leistung, dass zumindest für die getestete vorlagenbasierte Protokollanweisung der tatsächliche Leistungsunterschied zwischen JUL (langsam) und SLF4J + Protokollierung (schnell) ungefähr um den Faktor 10 zu liegen scheint. Genau wie Ceki sagte.
Ich kann auch eine andere Sache sehen, nämlich dass der getLogger()
Anruf von SLF4J viel teurer ist als der von JUL. (95 ms vs 0,3 ms, wenn mein Profiler genau ist). Das macht Sinn. SLF4J muss einige Zeit für die Bindung der zugrunde liegenden Protokollierungsimplementierung aufwenden. Das macht mir keine Angst. Diese Aufrufe sollten während der Lebensdauer einer Anwendung eher selten sein. Die Echtheit sollte in den eigentlichen Protokollaufrufen enthalten sein.
Schlußfolgerung
(Abschnitt hinzugefügt von nolan600 am 08-JUL-2012)
Vielen Dank für all Ihre Antworten. Im Gegensatz zu dem, was ich ursprünglich dachte, habe ich mich letztendlich für SLF4J für meine API entschieden. Dies basiert auf einer Reihe von Dingen und Ihrer Eingabe:
Es bietet Flexibilität bei der Auswahl der Protokollimplementierung zur Bereitstellungszeit.
Probleme mit mangelnder Flexibilität der JUL-Konfiguration bei der Ausführung auf einem Anwendungsserver.
SLF4J ist sicherlich viel schneller als oben beschrieben, insbesondere wenn Sie es mit Logback koppeln. Auch wenn dies nur ein grober Test war, habe ich Grund zu der Annahme, dass bei SLF4J + Logback viel mehr Anstrengungen zur Optimierung unternommen wurden als bei JUL.
Dokumentation. Die Dokumentation für SLF4J ist einfach viel umfassender und präziser.
Musterflexibilität. Während ich die Tests durchführte, wollte ich, dass JUL das Standardmuster von Logback nachahmt. Dieses Muster enthält den Namen des Threads. Es stellt sich heraus, dass JUL dies nicht sofort tun kann. Ok, ich habe es bis jetzt nicht verpasst, aber ich denke nicht, dass es etwas ist, das in einem Protokoll-Framework fehlen sollte. Zeitraum!
Die meisten (oder viele) Java-Projekte verwenden heutzutage Maven, daher ist das Hinzufügen einer Abhängigkeit nicht so wichtig, insbesondere wenn diese Abhängigkeit ziemlich stabil ist, dh ihre API nicht ständig ändert. Dies scheint für SLF4J zu gelten. Auch das SLF4J-Glas und seine Freunde sind klein.
Das Seltsame war also, dass ich mich über JUL ziemlich aufgeregt habe, nachdem ich ein bisschen mit SLF4J gearbeitet hatte. Ich bedaure immer noch, dass es bei JUL so sein muss. JUL ist alles andere als perfekt, macht aber irgendwie den Job. Nur nicht gut genug. Das Gleiche kann Properties
als Beispiel angeführt werden, aber wir denken nicht daran, dies zu abstrahieren, damit die Leute ihre eigene Konfigurationsbibliothek anschließen können und was Sie haben. Ich denke, der Grund dafür ist, dass es Properties
knapp über der Latte liegt, während das Gegenteil für JUL von heute gilt ... und in der Vergangenheit bei Null, weil es nicht existierte.
InternalLoggerFactory.java
.java.lang.System.Logger
wurde. Hierbei handelt es sich um eine Schnittstelle , die auf das gewünschte Protokollierungsframework umgeleitet werden kann, sofern dieses Framework aufholt und eine Implementierung dieser Schnittstelle bereitstellt. In Kombination mit der Modularisierung können Sie sogar eine Anwendung mit einer gebündelten JRE bereitstellen, die keine enthältjava.util.logging
, wenn Sie ein anderes Framework bevorzugen.Antworten:
Haftungsausschluss : Ich bin der Gründer von log4j-, SLF4J- und logback-Projekten.
Es gibt objektive Gründe, SLF4J zu bevorzugen. Zum einen gibt SLF4J dem Endbenutzer die Freiheit, das zugrunde liegende Protokollierungsframework auszuwählen . Darüber hinaus bevorzugen versierte Benutzer Logback, das Funktionen bietet, die über log4j hinausgehen , wobei Jul weit zurückfällt . In Bezug auf Funktionen kann Juli für einige Benutzer ausreichend sein, für viele andere jedoch nicht. Kurz gesagt, wenn die Protokollierung für Sie wichtig ist, möchten Sie SLF4J mit Logback als zugrunde liegende Implementierung verwenden. Wenn die Protokollierung unwichtig ist, ist Juli in Ordnung.
Als Oss-Entwickler müssen Sie jedoch die Vorlieben Ihrer Benutzer berücksichtigen und nicht nur Ihre eigenen. Daraus folgt, dass Sie SLF4J nicht einführen sollten, weil Sie davon überzeugt sind, dass SLF4J besser als jul ist, sondern weil die meisten Java-Entwickler derzeit (Juli 2012) SLF4J als Protokollierungs-API bevorzugen. Wenn Sie sich letztendlich nicht für die Meinung der Bevölkerung interessieren, sollten Sie die folgenden Fakten berücksichtigen:
Daher ist es in diesem Fall ein logischer Irrtum, "harte Fakten" über der öffentlichen Meinung zu halten, obwohl dies scheinbar mutig ist.
Wenn immer noch nicht überzeugt, macht JB Nizet ein zusätzliches und schlagkräftiges Argument:
Wenn Sie aus irgendeinem Grund die SLF4J-API hassen und ihre Verwendung den Spaß an Ihrer Arbeit zunichte macht, dann entscheiden Sie sich auf jeden Fall für jul. Schließlich gibt es Mittel, um jul auf SLF4J umzuleiten .
Übrigens ist die Jul-Parametrisierung mindestens zehnmal langsamer als die von SLF4J, was einen spürbaren Unterschied macht.
quelle
java.util.logging
wurde in Java 1.4 eingeführt. Zuvor gab es Verwendungszwecke für die Protokollierung. Deshalb gibt es viele andere Protokollierungs-APIs. Diese APIs wurden vor Java 1.4 stark genutzt und hatten daher einen großartigen Marktanteil, der bei der Veröffentlichung von 1.4 nicht nur auf 0 fiel.JUL hat nicht so toll angefangen, viele der Dinge, die Sie erwähnt haben, waren in 1.4 viel schlimmer und wurden erst in 1.5 besser (und ich denke auch in 6, aber ich bin mir nicht sicher).
JUL eignet sich nicht für mehrere Anwendungen mit unterschiedlichen Konfigurationen in derselben JVM (denken Sie an mehrere Webanwendungen, die nicht interagieren sollten). Tomcat muss durch einige Reifen springen, damit das funktioniert (JUL effektiv neu implementieren, wenn ich das richtig verstanden habe).
Sie können nicht immer beeinflussen, welches Protokollierungsframework Ihre Bibliotheken verwenden. Daher hilft die Verwendung von SLF4J (das eigentlich nur eine sehr dünne API-Schicht über anderen Bibliotheken ist) dabei, ein einigermaßen konsistentes Bild der gesamten Protokollierungswelt zu erhalten (sodass Sie das zugrunde liegende Protokollierungsframework festlegen können, während sich die Bibliothek weiterhin im selben System protokolliert).
Bibliotheken können sich nicht leicht ändern. Wenn eine frühere Version einer Bibliothek zur Verwendung von Protokollierungsbibliothek-X verwendet wurde, kann sie nicht einfach zu Protokollierungsbibliothek-Y (z. B. JUL) wechseln, selbst wenn letztere eindeutig überragend ist: Jeder Benutzer dieser Bibliothek müsste lernen das neue Protokollierungsframework und (zumindest) die Protokollierung neu konfigurieren. Das ist ein großes Nein-Nein, besonders wenn es den meisten Menschen keinen offensichtlichen Gewinn bringt.
Trotzdem denke ich, dass JUL heutzutage zumindest eine gültige Alternative zu anderen Protokollierungsframeworks ist.
quelle
Meiner Meinung nach besteht der Hauptvorteil bei der Verwendung einer Protokollierungsfassade wie slf4j darin, dass Sie dem Endbenutzer der Bibliothek die Wahl der konkreten Protokollierungsimplementierung überlassen, die er möchte, anstatt dem Endbenutzer Ihre Wahl aufzuerlegen.
Vielleicht hat er Zeit und Geld in Log4j oder LogBack (spezielle Formatierer, Appender usw.) investiert und zieht es vor, Log4j oder LogBack weiterhin zu verwenden, anstatt jul zu konfigurieren. Kein Problem: slf4j erlaubt das. Ist es eine kluge Wahl, Log4j über Juli zu verwenden? Vielleicht, vielleicht nicht. Aber es ist dir egal. Lassen Sie den Endbenutzer auswählen, was er bevorzugt.
quelle
Ich habe, wie Sie vermuten, mit JUL angefangen, weil es am einfachsten war, sofort loszulegen. Im Laufe der Jahre wünschte ich mir jedoch, ich hätte etwas mehr Zeit mit der Auswahl verbracht.
Mein Hauptproblem ist jetzt, dass wir eine beträchtliche Menge an 'Bibliotheks'-Code haben, der in vielen Anwendungen verwendet wird und alle JUL verwenden. Immer wenn ich diese Tools in einer Webdienst-App verwende, verschwindet die Protokollierung einfach oder geht an einen unvorhersehbaren oder seltsamen Ort.
Unsere Lösung bestand darin, dem Bibliothekscode eine Fassade hinzuzufügen, die bedeutete, dass sich die Aufrufe des Bibliotheksprotokolls nicht änderten, sondern dynamisch auf den verfügbaren Protokollierungsmechanismus umgeleitet wurden. Wenn sie in einem POJO-Tool enthalten sind, werden sie an JUL weitergeleitet, aber wenn sie als Web-App bereitgestellt werden, werden sie an LogBack umgeleitet.
Wir bedauern natürlich, dass der Bibliothekscode keine parametrisierte Protokollierung verwendet, diese kann jedoch bei Bedarf nachgerüstet werden.
Wir haben slf4j verwendet, um die Fassade zu bauen.
quelle
Ich habe jul gegen slf4j-1.7.21 über logback-1.1.7 ausgeführt und auf eine SSD, Java 1.8, Win64 ausgegeben
Juli lief 48449 ms, Logback 27185 ms für eine 1M-Schleife.
Ein bisschen mehr Geschwindigkeit und eine etwas schönere API sind für mich jedoch keine 3 Bibliotheken und 800 KB wert.
und
quelle
logger.info()
. Sie lähmen also absichtlich die Jul-Leistung, um einen Mangel in der Benutzeroberfläche von slf4j auszugleichen. Sie sollten stattdessen beide Methoden so codieren, wie sie idiomatisch codiert sind.