Das Logging-Framwork versucht so schnell wie möglich zu entscheiden, ob eine Nachricht bei einem eingestellten Log-Levels geloggt werden soll oder nicht. Ist die Stufe in der Produktion zum Beispiel auf WARNING, sind INFO-Meldungen zu ignorieren. Problematisch aus Performance-Sicht sind zum Beispiel aufwändig aufgebaute Log-Nachrichten, die dann sowieso nicht geloggt werden. Der Plus-Operator bei Strings gehört nicht zu den beachtlichen Zeitfressern, doch ein
log.info( "Open file: " + filename );
führt zur Laufzeit immer zu einer String-Konkatenation, egal, ob die erzeugte Nachricht später geloggt wird oder nicht.
JUL bietet zur Umgehung des Problems zwei Lösungen. Als erstes bietet die Logger-Klasse eine Testmethode boolean isLoggable(Level level), über die ein schneller Test durchgeführt werden kann:
if ( log.isLoggable(Level.INFO) )
log.info( "Open file: " + filename );
Natürlich kann info(…) nicht wissen, dass es auf jeden Fall loggen soll, daher findet der Test noch einmal statt. Eine allgemeine Überprüfung für alle Logging-Ausgaben bietet sich daher nicht an, sondern nur dann, wenn eine aufwändige Operation im Logging-Fall ausgeführt werden soll.
Die zweite Möglichkeit ist neu in Java 8. Sie nutzt Objekte vom Typ Supplier, die eine Implementierung enthalten, also etwa die Konkatenation. Im Prinzip hätte Oracle das auch schon vor Java 8 integrieren können, doch erst Lambda-Ausdrücke führen zu einer kompakten Schreibweise. Das sieht zum Beispiel so aus:
log.info( () -> { "Open file: " + filename } );
Hi Christian,
ich würde eher sl4j empfehlen, dort sähe dein Beispiel wie folgt aus:
log.info(„Open file: {}“,filename)
1. Wird gewrappt: „if ( log.isLoggable(Level.INFO) )“
2. keine unnötige Stringkonkatenation
mfg Lars
Hallo,
Wie wuerde denn sowas aussehen?
if (log.isLoggable(Level.INFO)) {
StringBuilder sb = new StringBuilder(256);
sb.append(„Open file: „).append(filename);
appendSomeMoreDetailsToStringBuilder(sb, filename);
log.info(sb.toString());
}
Reine Neugier. Ich habe mich noch nicht mit Lambda befasst,
die „altmodische“ Variante ist fuer mich, zumindest momentan,
aber verstaendlicher. Wie man bei der Lambda Variante
z.B. schlau Breakpoints setzt ohne Conditions ist mir auch noch nicht klar.
Mfg
Auf die String-Konkatenation kommt es doch gar nicht so sehr an, wichtig ist zu verstehen, dass schon der Aufbau der Log-Meldung relativ hole Kosten verursachen kann. Ich habe das Bsp. noch mal klarer gemacht:
log.info( „Open “ + file + „, size “ + Files.size( Path.get(file) ) );
(Ob + oder {} ist erst mal egal.)
Mit Lambda-Ausdrücken ist das perfekt debuggbar, da sich der Breakpoint dann in den Ausdruck reinbewegt, wenn geloggt wird, sonder drüber hinwegläuft und sich nicht in der Konkatenation oder so aufhält.
Geht es nur um das String-Concatenate, kann man die überladene Methode benutzen: log.log(Level.INFO, „Opening file {0} of {1} file(s)“, new Object[]{file, filecount});
Wird der Level nicht geloggt, wird auch kein neuer String erzeugt; Performance würde der Aufruf einer Methode kosten, z.B. files.size() statt die bereits initialisierte Variable filecount zu benutzen.
Im Beispiel oben, wo noch 2 Methoden aufgerufen werden (Files.size(), Path.get()), sind die Lambda-Ausdrücke eine gute Idee.
Kurzer Kommentar zur String-Konkatenation. Der Java-Compiler erzeugt aus dem + Operator automatisch ein StringBuilder-Konstrukt.
Bspw wird aus:
log.info( „Open file: “ + filename );
das hier:
StringBuilder sb = new StringBuilder();
log.info( sb.append(„Open file: „).append(filename).toString() );
Dass hierbei immer noch unnötigerweise eine StringBuilder-Instanz erzeugt wird, steht natürlich außer Frage.
Gruß, Witi