web-dev-qa-db-ger.com

Verbessert die Überprüfung von isDebugEnabled in log4j vor der Protokollierung die Leistung?

Ich verwende Log4J in meiner Anwendung für die Protokollierung. Bisher habe ich den Debug-Aufruf wie folgt verwendet:

Option 1:

logger.debug("some debug text");

einige Links empfehlen jedoch, zuerst isDebugEnabled() zu überprüfen, z. B .:

Option 2:

boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) {
    logger.debug("some debug text");
}

Meine Frage lautet also " Verbessert Option 2 die Leistung in irgendeiner Weise? ".

Weil in jedem Fall das Log4J-Framework die gleiche Prüfung auf debugEnabled hat. Für Option 2 kann es hilfreich sein, wenn Sie mehrere Debug-Anweisungen in einer einzelnen Methode oder Klasse verwenden, wobei das Framework die Methode isDebugEnabled() nicht mehrmals aufrufen muss (bei jedem Aufruf). In diesem Fall wird die Methode isDebugEnabled() nur einmal aufgerufen. Wenn Log4J für die Debug-Ebene konfiguriert ist, wird die Methode isDebugEnabled() tatsächlich zweimal aufgerufen:

  1. Wenn Sie der Variablen debugEnabled einen Wert zuweisen, und
  2. Wird aktuell von der logger.debug () -Methode aufgerufen.

Ich denke nicht, dass, wenn wir mehrere logger.debug()-Anweisungen in Methode oder Klasse schreiben und debug()-Methode gemäß Option 1 aufrufen, dies für das Log4J-Framework im Vergleich zu Option 2 einen Mehraufwand bedeutet. Da isDebugEnabled() eine sehr kleine Methode ist (in Bezug auf Code) Es könnte ein guter Kandidat für Inlining sein.

170
Silent Warrior

In diesem speziellen Fall ist Option 1 besser.

Die Guard-Anweisung (Überprüfung von isDebugEnabled()) dient dazu, eine möglicherweise teure Berechnung der Protokollnachricht zu verhindern, wenn die toString()-Methoden verschiedener Objekte aufgerufen werden und die Ergebnisse verkettet werden.

Im angegebenen Beispiel ist die Protokollnachricht eine konstante Zeichenfolge. Wenn der Logger verworfen wird, ist er genauso effizient wie die Überprüfung, ob der Logger aktiviert ist, und die Komplexität des Codes wird verringert, da weniger Verzweigungen vorhanden sind.

Besser noch ist die Verwendung eines aktuelleren Protokollierungsframeworks, bei dem die Protokollanweisungen eine Formatspezifikation und eine Liste von Argumenten enthalten, die vom Logger ersetzt werden sollen - aber "träge", nur wenn der Logger aktiviert ist. Dies ist der Ansatz von slf4j .

Siehe meine Antwort auf eine verwandte Frage für weitere Informationen und ein Beispiel, wie man so etwas mit log4j macht.

210
erickson

Da die Nachrichtenzeichenfolge in Option 1 eine Konstante ist, kann die Protokollierungsanweisung absolut nicht mit einer Bedingung umschlossen werden. Wenn die Protokollanweisung hingegen für die Fehlerbehebung aktiviert ist, werden Sie zweimal, einmal in der Methode isDebugEnabled() und einmal ausgewertet in debug() Methode. Die Kosten für das Aufrufen von isDebugEnabled() liegen in der Größenordnung von 5 bis 30 Nanosekunden, was für die meisten praktischen Zwecke vernachlässigbar sein dürfte. Daher ist Option 2 nicht wünschenswert, da sie Ihren Code verschmutzt und keine andere Verstärkung bietet.

30
Ceki

Die Verwendung von isDebugEnabled() ist für das Erstellen von Protokollnachrichten durch Verketten von Zeichenfolgen reserviert:

Var myVar = new MyVar();
log.debug("My var is " + myVar + ", value:" + myVar.someCall());

In Ihrem Beispiel gibt es jedoch keine Geschwindigkeitssteigerung, da Sie nur einen String protokollieren und keine Vorgänge wie Verkettung ausführen. Daher fügen Sie Ihrem Code nur Aufblasen hinzu und machen es schwieriger zu lesen.

Ich persönlich verwende die Aufrufe des Java 1.5-Formats in der String-Klasse folgendermaßen:

Var myVar = new MyVar();
log.debug(String.format("My var is '%s', value: '%s'", myVar, myVar.someCall()));

Ich bezweifle, dass es viel Optimierung gibt, aber es ist einfacher zu lesen.

Beachten Sie jedoch, dass die meisten Protokollierungs-APIs eine Formatierung wie folgt bieten: slf4j bietet beispielsweise Folgendes:

logger.debug("My var is {}", myVar);

das ist noch einfacher zu lesen.

15
Alex

Kurzfassung: Sie können auch die boolesche isDebugEnabled () - Prüfung durchführen.

Gründe dafür:
1- Bei komplizierter Logik/Zeichenfolge. wird zu Ihrer Debug-Anweisung hinzugefügt, Sie haben bereits das Einchecken.
2- Sie müssen die Anweisung in "komplexen" Debug-Anweisungen nicht selektiv einfügen. Alle Aussagen sind auf diese Weise enthalten.
3- Der Aufruf von log.debug führt vor der Protokollierung Folgendes aus:

if(repository.isDisabled(Level.DEBUG_INT))
return;

Dies ist im Grunde dasselbe wie ein Anrufprotokoll. oder katze. isDebugEnabled (). 

JEDOCH! Dies ist, was die log4j-Entwickler denken (wie es in ihrem Javadoc ist und Sie sollten es wahrscheinlich tun.) 

Dies ist die Methode 

public
  boolean isDebugEnabled() {
     if(repository.isDisabled( Level.DEBUG_INT))
      return false;
    return Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel());
  }

Dies ist der Javadoc dafür

/**
*  Check whether this category is enabled for the <code>DEBUG</code>
*  Level.
*
*  <p> This function is intended to lessen the computational cost of
*  disabled log debug statements.
*
*  <p> For some <code>cat</code> Category object, when you write,
*  <pre>
*      cat.debug("This is entry number: " + i );
*  </pre>
*
*  <p>You incur the cost constructing the message, concatenatiion in
*  this case, regardless of whether the message is logged or not.
*
*  <p>If you are worried about speed, then you should write
*  <pre>
*    if(cat.isDebugEnabled()) {
*      cat.debug("This is entry number: " + i );
*    }
*  </pre>
*
*  <p>This way you will not incur the cost of parameter
*  construction if debugging is disabled for <code>cat</code>. On
*  the other hand, if the <code>cat</code> is debug enabled, you
*  will incur the cost of evaluating whether the category is debug
*  enabled twice. Once in <code>isDebugEnabled</code> and once in
*  the <code>debug</code>.  This is an insignificant overhead
*  since evaluating a category takes about 1%% of the time it
*  takes to actually log.
*
*  @return boolean - <code>true</code> if this category is debug
*  enabled, <code>false</code> otherwise.
*   */
8
Andrew Carr

In Java 8 müssen Sie nicht isDebugEnabled() verwenden, um die Leistung zu verbessern.

https://logging.Apache.org/log4j/2.0/manual/api.html#Java_8_lambda_support_for_lazy_logging

import Java.util.logging.Logger;
...
Logger.getLogger("hello").info(() -> "Hello " + name);
7
cybaek

Wie bereits erwähnt, ist die Verwendung der Guard-Anweisung nur dann wirklich nützlich, wenn das Erstellen der Zeichenfolge ein zeitaufwändiger Aufruf ist. Spezifische Beispiele hierfür sind, wenn das Erstellen des Strings ein verzögertes Laden auslöst.

Es ist erwähnenswert, dass dieses Problem durch Verwendung von Simple Logging Facade für Java oder (SLF4J) - http://www.slf4j.org/manual.html umgangen werden kann. Dies ermöglicht Methodenaufrufe wie:

logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);

Dadurch werden die übergebenen Parameter nur in Strings konvertiert, wenn Debug aktiviert ist. SLF4J ist, wie der Name schon sagt, nur eine Fassade und die Logging-Aufrufe können an log4j übergeben werden.

Sie können auch sehr leicht "eine eigene" Version davon rollen.

Hoffe das hilft.

6
Pablojim

Option 2 ist besser.

Per se verbessert es die Leistung nicht. Es stellt jedoch sicher, dass die Leistung nicht abnimmt. Hier ist wie.

Normalerweise erwarten wir Logger.debug (someString);

Aber in der Regel, wenn die Anwendung wächst, wechseln viele Hände, besonders Anfänger, wie Sie sehen

logger.debug (str1 + str2 + str3 + str4);

und dergleichen.

Selbst wenn die Protokollebene auf ERROR oder FATAL gesetzt ist, kommt es zu einer Verkettung von Zeichenfolgen! Wenn die Anwendung viele DEBUG-Meldungen mit Zeichenfolgenverkettungen enthält, ist dies insbesondere bei jdk 1.4 oder darunter mit einem Performance-Schlag verbunden. (Ich bin nicht sicher, ob spätere Versionen von jdk intern alle Stringbuffer.append () ausführen).

Deshalb ist Option 2 sicher. Sogar die String-Verkettungen kommen nicht vor.

6
Sathya

Wie bei @erickson kommt es darauf an. Wenn ich mich erinnere, ist isDebugEnabled bereits in der debug()-Methode von Log4j eingebaut.
Solange Sie in Ihren Debug-Anweisungen keine teuren Berechnungen durchführen, z. B. eine Schleife für Objekte, Berechnungen ausführen und Zeichenfolgen verketten, sind Sie meiner Meinung nach in Ordnung.

StringBuilder buffer = new StringBuilder();
for(Object o : myHugeCollection){
  buffer.append(o.getName()).append(":");
  buffer.append(o.getResultFromExpensiveComputation()).append(",");
}
log.debug(buffer.toString());

wäre besser als 

if (log.isDebugEnabled(){
  StringBuilder buffer = new StringBuilder();
  for(Object o : myHugeCollection){
    buffer.append(o.getName()).append(":");
    buffer.append(o.getResultFromExpensiveComputation()).append(",");
  }
  log.debug(buffer.toString());
}
3
John Doe

Da wahrscheinlich viele Leute diese Antwort bei der Suche nach log4j2 sehen und fast alle aktuellen Antworten log4j2 oder die letzten Änderungen nicht berücksichtigen, sollte dies hoffentlich die Frage beantworten.

log4j2 unterstützt Supplier s (derzeit eigene Implementierung, laut Dokumentation ist jedoch die Verwendung der Lieferanten-Schnittstelle von Java in Version 3.0 geplant). Im manual können Sie etwas mehr darüber lesen. Auf diese Weise können Sie die Erstellung teurer Protokollnachrichten in einen Lieferanten übertragen, der die Nachricht nur erstellt, wenn sie protokolliert wird:

LogManager.getLogger().debug(() -> createExpensiveLogMessage());
2
Marcono1234

Es verbessert die Geschwindigkeit, da es üblich ist, Strings im Debugtext zu verketten, was teuer ist, z. B .:

boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) {
    logger.debug("some debug text" + someState);
}
2
Tom

Für eine single line verwende ich eine ternary innerhalb der Protokollierungsnachricht. Auf diese Weise mache ich die Verkettung nicht:

ej:

logger.debug(str1 + str2 + str3 + str4);

Ich mache:

logger.debug(logger.isDebugEnable()?str1 + str2 + str3 + str4:null);

Aber für mehrere Zeilen Code

ej.

for(Message mess:list) {
    logger.debug("mess:" + mess.getText());
}

Ich mache: 

if(logger.isDebugEnable()) {
    for(Message mess:list) {
         logger.debug("mess:" + mess.getText());
    }
}
2
rbeltrand

Mit Log4j2 können Sie Parameter in eine Meldungsvorlage formatieren, ähnlich wie String.format(), wodurch isDebugEnabled() nicht mehr erforderlich ist.

Logger log = LogManager.getFormatterLogger(getClass());
log.debug("Some message [myField=%s]", myField);

Beispiel für eine einfache log4j2.properties:

filter.threshold.type = ThresholdFilter
filter.threshold.level = debug
appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d %-5p: %c - %m%n
appender.console.filter.threshold.type = ThresholdFilter
appender.console.filter.threshold.level = debug
rootLogger.level = info
rootLogger.appenderRef.stdout.ref = STDOUT
0
Andre

Wenn Sie Option 2 verwenden, führen Sie eine boolesche Prüfung durch, die schnell ist. Bei Option 1 führen Sie einen Methodenaufruf aus (pushen Sachen auf den Stapel) und führen dann eine boolesche Prüfung durch, die immer noch schnell ist. Das Problem, das ich sehe, ist Konsistenz. Wenn einige Ihrer Debug- und Info-Anweisungen umschlossen sind und andere nicht, ist dies kein konsistenter Codestil. Außerdem könnte jemand später die Debug-Anweisung so ändern, dass verkettete Zeichenfolgen eingeschlossen werden, was immer noch ziemlich schnell ist. Ich fand heraus, dass wir, als wir die Debug- und Info-Anweisung in einer großen Anwendung auspackten, ein paar Prozentpunkte an Leistung einsparen. Nicht viel, aber genug, um die Arbeit wert zu machen. Ich habe jetzt ein paar Makros in IntelliJ eingerichtet, um automatisch umschlossene Debug- und Info-Anweisungen für mich zu generieren.

0
Javamann

Ich würde empfehlen, Option 2 für die meisten de facto zu verwenden, da es nicht sehr teuer ist.

Fall 1: log.debug ("one string")

Fall 2: log.debug ("one string" + "two string" + object.toString + object2.toString)

Zum Zeitpunkt des Aufrufs muss die Parameterzeichenfolge in log.debug (sei es CASE 1 oder Case2) ausgewertet werden. Das ist es, was jeder unter "teuer" versteht. Wenn Sie eine Bedingung haben, 'isDebugEnabled ()', müssen diese nicht ausgewertet werden, in der die Leistung gespeichert wird.

0
Jolly1234

Seit 2.x ist diese Prüfung in Apache Log4j integriert, sodass isDebugEnabled() nicht mehr erforderlich ist. Machen Sie einfach eine debug() und die Meldungen werden unterdrückt, wenn sie nicht aktiviert sind.

0
ha9u63ar