it-swarm.com.de

Warum nicht Java.util.logging verwenden?

Zum ersten Mal in meinem Leben bin ich in einer Position, in der ich eine Java -API schreibe, die Open-Sourcing sein wird. 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 sollte. Ich habe einige Nachforschungen angestellt und mit den Informationen, die ich habe, bin ich nur noch verwirrter. Daher dieser Beitrag.

Da ich von JUL komme, bin ich voreingenommen. Mein Wissen über den Rest ist nicht so groß.

Aus den Recherchen, die ich gemacht habe, habe ich folgende Gründe herausgefunden, warum die Leute JUL nicht mögen:

  1. "Ich habe mit der Entwicklung in Java begonnen, lange bevor Sun JUL veröffentlichte, und es war für mich einfacher, mit Logging-Framework-X fortzufahren, als etwas Neues zu lernen" . Hmm. Ich mache keine Witze, das sagen die Leute. Mit diesem Argument könnten wir alle COBOL machen. (Allerdings kann ich mit Sicherheit sagen, dass ich selbst ein fauler Typ bin.)

  2. "Ich mag die Namen der Protokollierungsstufen in JUL nicht" . Ok, im Ernst, das ist einfach nicht genug, um eine neue Abhängigkeit einzuführen.

  3. "Ich mag das Standardformat der Ausgabe von JUL nicht" . Hmm. Dies ist nur die Konfiguration. Sie müssen nicht einmal Code-weise etwas tun. (Richtig, früher mussten Sie möglicherweise Ihre eigene Formatter-Klasse erstellen, um es richtig zu machen).

  4. "Ich benutze andere Bibliotheken, die ebenfalls das Logging-Framework-X verwenden. Ich dachte, es wäre einfacher, nur dieses zu verwenden" . Dies ist ein zirkuläres Argument, nicht wahr? Warum verwendet 'everybody' das Logging-Framework-X und nicht JUL?

  5. "Alle anderen verwenden das Logging-Framework-X" . Dies ist für mich nur ein Sonderfall. 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) besteht darin, dass es in der Vergangenheit mehrere Protokollierungsimplementierungen gab, und der Grund dafür geht meiner Ansicht nach in die Zeit vor JUL zurück. Wenn JUL perfekt wäre, gäbe es keine Holzfassaden, oder was? Um die Sache noch unübersichtlicher zu machen, ist JUL in gewissem Maße eine Fassade selbst, über die Handler, Formatierer und sogar der LogManager ausgetauscht werden können.

Sollten wir uns nicht fragen, warum sie überhaupt notwendig waren, anstatt mehrere Möglichkeiten 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, die ich sehen kann, die möglicherweise echte Probleme mit JUL sind:

  1. Leistung . Einige sagen, dass die Leistung in SLF4J dem Rest überlegen ist. Dies scheint mir ein Fall vorzeitiger Optimierung zu sein. Wenn Sie Hunderte von Megabyte pro Sekunde aufzeichnen müssen, bin ich mir nicht sicher, ob Sie auf dem richtigen Weg sind. JUL hat sich ebenfalls weiterentwickelt und die Tests, die Sie mit Java 1.4 durchgeführt haben, sind möglicherweise nicht mehr gültig. Sie können darüber lesen hier und dieses Update hat es in Java 7 geschafft. Viele sprechen auch über den Overhead der String-Verkettung in Protokollierungsmethoden. Die vorlagenbasierte Protokollierung vermeidet diese Kosten und ist auch in JUL verfügbar. Persönlich schreibe ich nie wirklich vorlagenbasierte Protokollierung. Zu faul dafür. Zum Beispiel, wenn ich das mit JUL mache:

    log.finest("Lookup request from username=" + username 
       + ", valueX=" + valueX
       + ", valueY=" + valueY));
    

    mein IDE wird mich warnen und um Erlaubnis bitten, dass es sich ändern 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.

    Also schreibe ich solche Aussagen nicht selbst, das macht die IDE.

    Abschließend zum Thema Leistung habe ich nichts gefunden, was darauf hindeuten könnte, dass die Leistung von JUL im Vergleich zur Konkurrenz nicht in Ordnung ist.

  2. Konfiguration aus Klassenpfad . Standardmäßig kann JUL keine Konfigurationsdatei aus dem Klassenpfad laden. Es ist ein wenige Codezeilen , um dies zu tun. Ich kann sehen, warum das vielleicht ärgerlich ist, aber die Lösung ist kurz und einfach.

  3. Verfügbarkeit von Ausgabehandlern . JUL wird mit 5 Ausgabehandlern ausgeliefert: Konsole, Dateistream, Socket und Speicher. Diese können erweitert oder neue geschrieben werden. Dies kann beispielsweise das Schreiben in das UNIX/Linux-Syslog und das Windows-Ereignisprotokoll sein. Ich persönlich hatte diese Anforderung noch nie und habe sie auch nie benutzt gesehen, aber ich kann mit Sicherheit sagen, warum sie eine nützliche Funktion sein kann. Logback wird zum Beispiel mit einem Appender für Syslog geliefert. Trotzdem würde ich das argumentieren

    1. 99,5% des Bedarfs an Ausgabezielen werden von der Standardausstattung von JUL abgedeckt.
    2. Spezielle Bedürfnisse könnten von benutzerdefinierten Handlern zusätzlich zu JUL und nicht zusätzlich 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 mache mir wirklich Sorgen, dass ich etwas übersehen habe. Die Verwendung anderer Protokollierungsfassaden und Protokollierungsimplementierungen als JUL ist so weit verbreitet, dass ich zu dem Schluss kommen muss, dass ich es bin, der einfach nicht versteht. Ich fürchte, das wäre 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 Strom 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 los ist, das all die Unruhe rechtfertigt. Werde ich mich selbst sabotieren, indem ich JUL für meine Bibliothek auswähle?

Leistung testen

(Abschnitt hinzugefügt von nolan600 am 07. Juli 2012)

Es gibt eine Referenz von Ceki, wonach die Parametrisierung von SLF4J 10-mal oder schneller ist als die von JUL. Also habe ich angefangen, ein paar einfache Tests durchzuführen. Die Behauptung ist auf den ersten Blick 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 msec, also ist weniger besser. Der 10-fache Leistungsunterschied liegt also zunächst tatsächlich ziemlich nahe. Meine erste Reaktion: Das ist viel!

Hier ist der Kern des Tests. Wie zu sehen ist, werden eine Ganzzahl und ein String in einer Schleife aufgebaut, die dann in der log-Anweisung 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 log-Anweisung sowohl einen primitiven Datentyp (in diesem Fall einen int) als auch einen komplexeren Datentyp (in diesem Fall einen String) hat. Ich bin mir nicht sicher, ob es darauf ankommt, aber Sie haben ihn.)

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 zu null device umgeleitet.

Vorsichtig stellt sich jedoch heraus, dass JUL die meiste Zeit in getSourceClassName() verbringt, da JUL standardmäßig den Namen der Quellklasse in der Ausgabe ausgibt, während dies bei der Rückmeldung nicht der Fall ist. Wir vergleichen also Äpfel und Orangen. Ich muss den Test erneut durchführen und die Protokollierungsimplementierungen auf ähnliche Weise konfigurieren, sodass sie tatsächlich dasselbe Material ausgeben. Ich vermute jedoch, dass SLF4J + Logback weiterhin die Nase vorn hat, aber weit von den oben angegebenen Anfangszahlen entfernt ist. Bleib dran.

Übrigens: Der Test war das erste Mal, dass ich 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. Juli 2012)

Wie sich herausstellt, spielt es für die Leistung keine Rolle, wie Sie Ihr Muster in JUL konfigurieren, d. H. 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 ergab, dass der Logger immer noch viel Zeit mit Aufrufen von getSourceClassName() verbracht hat, auch wenn dies nicht Teil meines Musters war. Das Muster spielt keine Rolle.

Daher komme ich zum Thema Leistung zu dem Schluss, dass es zumindest für die getestete vorlagenbasierte Protokollanweisung einen Faktor von 10 für den tatsächlichen Leistungsunterschied zwischen JUL (langsam) und SLF4J + Logback (schnell) zu geben scheint. Genau wie Ceki gesagt hat.

Ich kann auch eine andere Sache sehen, nämlich, dass der getLogger() -Aufruf 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 in der Lebensdauer einer Anwendung eher selten sein. Die Echtheit sollte in den tatsächlichen Protokollaufrufen sein.

Schlußfolgerung

(Abschnitt hinzugefügt von nolan600 am 08. Juli 2012)

Vielen Dank für alle Ihre Antworten. Im Gegensatz zu dem, was ich ursprünglich dachte, habe ich mich für SLF4J als API entschieden. Dies basiert auf einer Reihe von Dingen und Ihrer Eingabe:

  1. Es bietet Flexibilität bei der Auswahl der Protokollimplementierung zum Bereitstellungszeitpunkt.

  2. Probleme mit mangelnder Flexibilität der JUL-Konfiguration beim Ausführen in einem Anwendungsserver.

  3. SLF4J ist sicherlich viel schneller, wie oben beschrieben, insbesondere wenn Sie es mit Logback koppeln. Auch wenn dies nur ein grober Test war, habe ich Grund zu der Annahme, dass die Optimierung für SLF4J + Logback wesentlich aufwendiger war als für JUL.

  4. Dokumentation. Die Dokumentation für SLF4J ist einfach viel umfassender und präziser.

  5. Musterflexibilität. Als ich die Tests durchführte, wollte ich, dass JUL das Standardmuster von Logback nachahmt. Dieses Muster enthält den Namen des Threads. Es hat sich herausgestellt, dass JUL dies nicht sofort tun kann. Ok, ich habe es bis jetzt nicht verpasst, aber ich denke nicht, dass es eine Sache ist, die in einem Log-Framework fehlen sollte. Zeitraum!

  6. Die meisten (oder viele) Java Projekte verwenden heutzutage Maven, daher ist das Hinzufügen einer Abhängigkeit keine große Sache, insbesondere wenn diese Abhängigkeit ziemlich stabil ist, d. H. Die API nicht ständig ändert. Dies scheint für SLF4J zuzutreffen. Auch das SLF4J-Gefäß und seine Freunde sind klein.

Das Seltsame war, dass ich mich über JUL ziemlich aufgeregt hatte, 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 ganz gut genug. Das Gleiche gilt für Properties als Beispiel, aber wir denken nicht darüber nach, dies zu abstrahieren, damit die Leute ihre eigene Konfigurationsbibliothek einbinden können und was Sie haben. Ich denke, der Grund dafür ist, dass Properties direkt über der Bar hereinkommt, während das Gegenteil für JUL von heute zutrifft ... und in der Vergangenheit auf Null hereinkam, weil es nicht existierte.

320
peterh

Haftungsausschluss : Ich bin der Gründer von log4j-, SLF4J- und Logback-Projekten.

Es gibt objektive Gründe, SLF4J zu bevorzugen. Zum einen erlaubt SLF4J dem Endbenutzer die freie Auswahl des zugrunde liegenden Protokollierungsframeworks . Darüber hinaus bevorzugen versierte Benutzer Rückmeldung, die Funktionen bietet, die über log4j hinausgehen , wobei j.u.l weit hinterherhinkt. Die Funktionalität von j.u.l kann 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 Rückmeldung als zugrunde liegende Implementierung verwenden. Wenn die Protokollierung unwichtig ist, ist j.u.l 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 übernehmen sollten, weil Sie davon überzeugt sind, dass SLF4J besser ist als jul, sondern weil die meisten Java Entwickler derzeit (Juli 2012) SLF4J als ihr bevorzugen Protokollierungs-API: Wenn Sie sich letztendlich nicht für die allgemeine Meinung interessieren, sollten Sie die folgenden Fakten berücksichtigen:

  1. diejenigen, die j.u.l bevorzugen, tun dies aus Bequemlichkeitsgründen, weil j.u.l mit dem JDK gebündelt ist. Meines Wissens gibt es keine anderen objektiven Argumente für j.u.l.
  2. ihre eigene Präferenz für j.u.l ist genau das, eine Präferenz.

Daher ist es in diesem Fall ein logischer Trugschluss, "harte Fakten" über die öffentliche Meinung zu stellen, obwohl dies mutig erscheint.

Wenn immer noch nicht überzeugt, macht JB Nizet ein zusätzliches und starkes Argument:

Mit der Ausnahme, dass der Endbenutzer diese Anpassung bereits für seinen eigenen Code oder eine andere Bibliothek durchgeführt hat, die log4j oder logback verwendet. j.u.l ist erweiterbar, aber es ist umständlich, j.u.l, log4j und Gott weiß nur, welches andere Protokollierungsframework er verwendet, da er vier Bibliotheken verwendet, die vier verschiedene Protokollierungsframeworks verwenden. Mit SLF4J können Sie ihm erlauben, die gewünschten Protokollierungsframeworks zu konfigurieren, nicht das von Ihnen ausgewählte. Denken Sie daran, dass ein typisches Projekt unzählige Bibliotheken verwendet und nicht nur Ihre .

Wenn Sie aus irgendeinem Grund die SLF4J-API hassen und sie verwenden, wird Ihnen die Arbeit Spaß machen, dann entscheiden Sie sich auf jeden Fall für j.u.l. Immerhin gibt es Mittel, um j.u.l zu SLF4J umzuleiten .

Übrigens ist die Parametrisierung von j.u.l mindestens 10-mal langsamer als die von SLF4J, was letztendlich einen spürbaren Unterschied macht.

183
Ceki
  1. Java.util.logging wurde in Java 1.4 eingeführt. Davor gab es Verwendungszwecke für die Protokollierung, daher gibt es viele andere Protokollierungs-APIs. Diese APIs wurden häufig vor Java 1.4) verwendet und hatte damit einen tollen Marktanteil, der bei der Veröffentlichung von 1.4 nicht nur auf 0 gefallen ist.

  2. JUL hat nicht so toll angefangen, viele der Dinge, die Sie erwähnt haben, waren in 1.4 viel schlimmer und wurden in 1.5 nur besser (und ich denke auch in 6, aber ich bin mir nicht sicher).

  3. 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 Rahmen springen, um dies zum Laufen zu bringen (effektiv JUL neu implementieren, wenn ich das richtig verstanden habe).

  4. 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 konsistentes Bild der gesamten Protokollierungswelt zu erhalten (sodass Sie das zugrunde liegende Protokollierungsframework festlegen können, während die Protokollierung der Bibliothek im selben System erfolgt).

  5. Bibliotheken können nicht einfach geändert werden. Wenn eine frühere Version einer Bibliothek die Protokollierungsbibliothek-X verwendet, kann sie nicht einfach auf die Protokollierungsbibliothek-Y (z. B. JUL) umschalten, auch wenn letztere eindeutig überlegen ist: Jeder Benutzer dieser Bibliothek müsste lernen das neue Protokollierungsframework und konfigurieren (zumindest) ihre Protokollierung neu. Das ist ein großes Nein, besonders wenn es den meisten Menschen keinen offensichtlichen Gewinn bringt.

Trotzdem denke ich, dass JUL zumindest eine gültige Alternative zu anderen Protokollierungs-Frameworks ist.

29
Joachim Sauer

IMHO ist der Hauptvorteil bei der Verwendung einer Protokollierungsfassade wie slf4j, dass Sie dem Endbenutzer der Bibliothek die Wahl überlassen, welche konkrete Protokollierungsimplementierung er wünscht, anstatt dem Endbenutzer Ihre Wahl aufzuerlegen.

Vielleicht hat er Zeit und Geld in Log4j oder LogBack (spezielle Formatierer, Appender usw.) investiert und bevorzugt es, weiterhin Log4j oder LogBack zu verwenden, anstatt jul zu konfigurieren. Kein Problem: slf4j erlaubt das. Ist es eine gute Wahl, Log4j über Juli zu verwenden? Vielleicht, vielleicht nicht. Aber es ist dir egal. Lassen Sie den Endbenutzer auswählen, was er bevorzugt.

27
JB Nizet

Ich habe wie Sie vermutlich mit JUL angefangen, weil es am einfachsten war, sofort loszulegen. Im Laufe der Jahre bin ich jedoch zu dem Wunsch gekommen, ich hätte ein wenig mehr Zeit für die Auswahl aufgewendet.

Mein Hauptproblem ist jetzt, dass wir eine beträchtliche Menge an 'Bibliotheks'-Code haben, der in vielen Anwendungen verwendet wird, und alle verwenden JUL. Jedes Mal, wenn ich diese Tools in einer App vom Typ Webdienst 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 die Aufrufe des Bibliotheksprotokolls nicht geändert wurden, sondern dynamisch an 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 weitergeleitet.

Wir bedauern natürlich, dass der Bibliothekscode keine parametrisierte Protokollierung verwendet. Diese kann jetzt bei Bedarf nachgerüstet werden.

Wir haben die Fassade mit slf4j erstellt.

6
OldCurmudgeon

Ich habe jul gegen slf4j-1.7.21 über logback-1.1.7 ausgeführt und auf eine SSD ausgegeben, Java 1.8, Win64

jul 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 3 Bibliotheken und 800 KB nicht wert.

package log;

import Java.util.logging.Level;
import Java.util.logging.Logger;

public class LogJUL
{
    final static Logger logger = Logger.getLogger(LogJUL.class.getSimpleName());

    public static void main(String[] args) 
    {
        int N = 1024*1024;

        long l = System.currentTimeMillis();

        for (int i = 0; i < N; i++)
        {
            Long lc = System.currentTimeMillis();

            Object[] o = { lc };

            logger.log(Level.INFO,"Epoch time {0}", o);
        }

        l = System.currentTimeMillis() - l;

        System.out.printf("time (ms) %d%n", l);
    }
}

und

package log;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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


    public static void main(String[] args) 
    {
        int N = 1024*1024;

        long l = System.currentTimeMillis();

        for (int i = 0; i < N; i++)
        {
            Long lc = System.currentTimeMillis();

            logger.info("Epoch time {}", lc);
        }

        l = System.currentTimeMillis() - l;

        System.out.printf("time (ms) %d%n", l);
    }

}
2
weberjn