Built-in string formatieren vs string-Verkettung als logging-parameter
Ich bin mit SonarLint, der zeigt mir ein Problem in der folgenden Zeile.
LOGGER.debug("Comparing objects: " + object1 + " and " + object2);
Seite-Hinweis: Die Methode, enthält diese Zeile kann aufgerufen werden, ziemlich oft.
Die Beschreibung für dieses Problem ist
"Voraussetzungen" und die Protokollierung Argumente sollte nicht verlangen, evaluation
(squid:S2629)Passing message Argumente, die erfordern, dass eine weitere Auswertung in eine Guave
com.google.common.base.Voraussetzungen überprüfen kann, im Ergebnis eine Leistung
Strafe. Das ist, weil, ob oder nicht Sie sind notwendig, jedem argument
muss gelöst werden, bevor die Methode tatsächlich aufgerufen wird.Ähnlich, vorbei verkettet strings in eine logging-Methode kann auch
entstehen unnötige performance-Einbußen, weil die Verkettung wird
durchgeführt, jedes mal, wenn die Methode aufgerufen wird, ob das Protokoll
Niveau ist niedrig genug, um die Meldung angezeigt.Stattdessen sollten Sie Ihren code strukturieren zu übergeben, statische oder pre-berechnet
Werte in die Voraussetzungen, Bedingungen zu überprüfen und die Protokollierung der Anrufe.Insbesondere die integrierten string-Formatierung verwendet werden soll, statt
die string-Verkettung, und wenn die Botschaft ist das Ergebnis einer Methode
Anruf, dann die Voraussetzungen übersprungen werden sollen altoghether, und die
relevant sollte die Ausnahme sein, bedingt geworfen statt.Nicht Kompatiblen Code Beispiel
logger.log(Level.DEBUG, "Something went wrong: " + message); //Noncompliant; string concatenation performed even when log level too high to show DEBUG messages LOG.error("Unable to open file " + csvPath, e); //Noncompliant Preconditions.checkState(a > 0, "Arg must be positive, but got " + a); //Noncompliant. String concatenation performed even when a > 0 Preconditions.checkState(condition, formatMessage()); //Noncompliant. formatMessage() invoked regardless of condition Preconditions.checkState(condition, "message: %s", formatMessage()); //Noncompliant
Konforme Lösung
logger.log(Level.SEVERE, "Something went wrong: %s", message); //String formatting only applied if needed logger.log(Level.SEVERE, () -> "Something went wrong: " + message); //since Java 8, we can use Supplier , which will be evaluated lazily LOG.error("Unable to open file {}", csvPath, e); if (LOG.isDebugEnabled() { LOG.debug("Unable to open file " + csvPath, e); //this is compliant, because it will not evaluate if log level is above debug. } Preconditions.checkState(arg > 0, "Arg must be positive, but got %d", a); //String formatting only applied if needed if (!condition) { throw new IllegalStateException(formatMessage()); //formatMessage() only invoked conditionally } if (!condition) { throw new IllegalStateException("message: " + formatMessage()); }
Bin ich mir nicht 100% sicher, ob ich verstehe dieses Recht. Warum also ist dieses wirklich ein Problem. Vor allem der Teil über die Leistungseinbußen bei Verwendung der string-Verkettung. Da ich oft gelesen, dass die string-Verkettung ist schneller als der Formatierung.
EDIT: Vielleicht kann jemand erklären mir den Unterschied zwischen
LOGGER.debug("Comparing objects: " + object1 + " and " + object2);
UND
LOGGEr.debug("Comparing objects: {} and {}",object1, object2);
ist im hintergrund. Weil ich denke der String wird erstellt, bevor es an die Methode übergeben wird. Richtig? Also für mich gibt es keinen Unterschied. Aber offensichtlich Irre ich mich da SonarLint beschwert sich über es
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich glaube, Sie haben Ihre Antwort.
Verkettung berechnet vorher den Zustand prüfen. So rufen Sie Ihre logging-framework 10K mal bedingt, und alle von Ihnen zu false ausgewertet, Sie werden verketten, 10K mal ohne Grund.
Überprüfen Sie auch dieses Thema. Und überprüfen Icaro Antwort-Kommentare.
Werfen Sie einen Blick auf StringBuilder zu.
Betrachten Sie das folgende logging-Anweisung :
was ist das 'debug' ?
Dies ist die Ebene der logging-Anweisung und nicht der level des LOGGERS.
Sehen Sie, es gibt 2 Ebenen :
a) einer der logging-Anweisung (die debug hier) :
b) ist Eine Ebene des LOGGERS. Also, was ist das Niveau der LOGGER-Objekt :
Dies muss auch definiert werden, im code oder in einigen xml , sonst dauert es level vom Vorgänger .
Nun, warum erzähle ich all dies ?
Nun der logging-Anweisung verwendet wird, ausgedruckt werden (oder in eher technischen Begriff senden, um seine 'appender'), wenn und nur wenn :
Mögliche Werte von einer Ebene kann
(Es werden paar mehr, je nach logging-framework)
Nun lasst uns zurück zur Frage :
wird immer führen, um die Erstellung von string-auch wenn wir finden, dass "Niveau-Regel' oben erklärt ausfällt.
Jedoch
wird nur Ergebnis in der string-Bildung, wenn 'Ebene Regel oben erklärt" erfüllt.
Also, was ist smarter ?
Konsultieren Sie diese url.
String-Verkettung bedeutet
LOGGER.info("Das Programm begann am" + new Date());
Gebaut in der Formatierung der logger-Mittel
LOGGER.info("Das Programm gestartet auf {}", new Date());
sehr guten Artikel um den Unterschied zu verstehen
http://dba-presents.com/index.php/jvm/java/120-use-the-built-in-formatting-to-construct-this-argument
Date
Klasse wurde ersetzt durchInstant
. Siehe Oracle-Tutorial.