web-dev-qa-db-fra.com

Dans log4j, la vérification de isDebugEnabled avant la journalisation améliore-t-elle les performances?

J'utiliseLog4Jdans mon application pour la journalisation. Auparavant, j'utilisais un appel de débogage comme:

Option 1:

logger.debug("some debug text");

mais certains liens suggèrent qu'il est préférable de vérifier d'abord isDebugEnabled(), comme: 

Option 2:

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

Ma question est donc la suivante: " L’option 2 améliore-t-elle les performances? ".

Parce que dans tous les cas, les infrastructures Log4J ont la même vérification pour debugEnabled. Pour l'option 2, il peut être avantageux d'utiliser plusieurs instructions de débogage dans une seule méthode ou une seule classe, lorsque l'infrastructure n'a pas besoin d'appeler la méthode isDebugEnabled() plusieurs fois (à chaque appel). dans ce cas, il appelle la méthode isDebugEnabled() une seule fois, et si Log4J est configuré pour déboguer le niveau, il appelle en fait la méthode isDebugEnabled() deux fois:

  1. En cas d’affectation de valeur à la variable debugEnabled, et
  2. En réalité appelé par la méthode logger.debug ().

Je ne pense pas que si nous écrivons plusieurs instructions logger.debug() dans une méthode ou une classe et que nous appelions la méthode debug() conformément à l'option 1, il s'agit d'une surcharge pour le framework Log4J par rapport à l'option 2. Étant donné que isDebugEnabled() est une très petite méthode (en termes de code) , cela pourrait être un bon candidat pour l’inline.

170
Silent Warrior

Dans ce cas particulier, l'option 1 est préférable.

L'instruction de garde (check isDebugEnabled()) permet d'empêcher le calcul potentiellement coûteux du message de journal lorsqu'il implique l'appel des méthodes toString() de divers objets et la concaténation des résultats.

Dans l'exemple donné, le message de journal est une chaîne constante. Par conséquent, le laisser ignorer est tout aussi efficace que de vérifier si le journal est activé et réduit la complexité du code car il y a moins de branches.

Mieux encore, utilisez un cadre de journalisation plus à jour, dans lequel les instructions de journalisation prennent une spécification de format et une liste d'arguments à substituer par le consignateur, mais "paresseusement" uniquement si ce dernier est activé. C'est l'approche adoptée par slf4j .

Voir ma réponse à une question connexe pour plus d'informations et un exemple de ce type d'opération avec log4j.

210
erickson

Comme dans l'option 1, la chaîne de message est une constante, il n'y a absolument aucun avantage à encapsuler l'instruction de journalisation avec une condition; au contraire, si l'instruction de journalisation est activée pour le débogage, vous évaluerez deux fois, une fois dans la méthode isDebugEnabled() et une fois. dans la méthode debug(). Le coût d'invocation de isDebugEnabled() est de l'ordre de 5 à 30 nanosecondes, ce qui devrait être négligeable dans la plupart des cas. Ainsi, l'option 2 n'est pas souhaitable car elle pollue votre code et ne procure aucun autre gain.

29
Ceki

L'utilisation de isDebugEnabled() est réservée à la construction de messages de journal en concaténant des chaînes:

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

Cependant, dans votre exemple, il n'y a pas de gain de vitesse car vous ne vous connectez qu'une chaîne et ne procédez pas à des opérations telles que la concaténation. Par conséquent, vous ne faites que gonfler votre code et le rendre plus difficile à lire.

Personnellement, j'utilise les appels au format Java 1.5 dans la classe String comme ceci:

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

Je doute qu'il y ait beaucoup d'optimisation mais c'est plus facile à lire.

Notez cependant que la plupart des API de journalisation proposent une mise en forme immédiate: slf4j, par exemple, fournit les éléments suivants:

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

ce qui est encore plus facile à lire.

15
Alex

Version courte: Vous pourriez aussi bien faire la vérification booléenne isDebugEnabled ().

Les raisons:
1- Si logique/concaténation compliquée. est ajouté à votre déclaration de débogage, vous aurez déjà la vérification en place.
2- Il n'est pas nécessaire d'inclure sélectivement l'instruction sur les instructions de débogage "complexes". Toutes les déclarations sont incluses de cette façon.
3- L'appel de log.debug exécute les opérations suivantes avant la journalisation:

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

C'est fondamentalement la même chose que d'appeler le journal. ou un chat. isDebugEnabled (). 

TOUTEFOIS! C’est ce que pensent les développeurs de log4j (comme dans leur javadoc et vous devriez probablement vous y fier.) 

C'est la méthode 

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

Ceci est le javadoc pour cela

/**
*  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

En Java 8, vous n'avez pas besoin d'utiliser isDebugEnabled() pour améliorer les performances.

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

Comme d'autres l'ont mentionné, l'utilisation de l'instruction Guard n'est vraiment utile que si la création de la chaîne prend beaucoup de temps. Des exemples spécifiques de cela sont lors de la création de la chaîne va déclencher un chargement paresseux.

Il est à noter que ce problème peut être évité en utilisant Simple Logging Facade pour Java ou (SLF4J) - http://www.slf4j.org/manual.html . Cela permet des appels de méthode tels que:

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

Cela ne convertira les paramètres passés en chaînes que si le débogage est activé. Comme son nom l'indique, SLF4J n'est qu'une façade et les appels de journalisation peuvent être passés à log4j.

Vous pouvez également très facilement "rouler votre propre" version de cela.

J'espère que cela t'aides.

6
Pablojim

L'option 2 est meilleure.

En soi, cela n'améliore pas les performances. Mais cela garantit que les performances ne se dégradent pas. Voici comment.

Normalement nous attendons Logger.debug (someString);

Mais d’habitude, à mesure que l’application grandit et change de main en main, en particulier pour les développeurs novices, vous pouvez voir:

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

etc.

Même si log level est défini sur ERROR ou FATAL, la concaténation de chaînes a bien lieu! Si l'application contient de nombreux messages de niveau DEBUG avec des concaténations de chaînes, il faut assurément une performance, en particulier avec jdk version 1.4 ou inférieure. (Je ne sais pas si les versions ultérieures de jdk internall utilisent stringbuffer.append ()).

C'est pourquoi l'option 2 est sans danger. Même les concaténations de chaînes ne se produisent pas.

6
Sathya

Comme @erickson cela dépend. Si je me rappelle bien, isDebugEnabled est déjà construit dans la méthode debug() de Log4j.
Tant que vous ne faites pas de calculs coûteux dans vos instructions de débogage, comme une boucle sur des objets, effectuez des calculs et concaténez des chaînes, tout va bien à mon avis.

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

serait mieux comme 

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

Étant donné que de nombreuses personnes consultent probablement cette réponse lors de la recherche de log4j2 et que presque toutes les réponses actuelles ne tiennent pas compte de log4j2 ou de modifications récentes, cela devrait, espérons-le, répondre à la question.

log4j2 prend en charge Supplier s (actuellement leur propre implémentation, mais selon la documentation, il est prévu d'utiliser l'interface fournisseur de Java dans la version 3.0). Vous pouvez en lire un peu plus à ce sujet dans le manual . Cela vous permet de créer un message coûteux dans un fournisseur qui ne le crée que s'il doit être consigné:

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

Cela améliore la vitesse, car il est courant de concaténer des chaînes dans le texte de débogage, ce qui coûte cher, par exemple:

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

Pour un single line , j'utilise un message ternaire à l'intérieur du message de journalisation. Ainsi, je ne fais pas la concaténation

ej:

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

Je fais:

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

Mais pour plusieurs lignes de code

ej.

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

Je fais:  

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

Log4j2 vous permet de formater les paramètres dans un modèle de message, similaire à String.format(), éliminant ainsi la nécessité de faire isDebugEnabled().

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

Exemple de log4j2.properties simple:

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

Si vous utilisez l'option 2, vous effectuez une vérification booléenne rapide. Dans la première option, vous effectuez un appel de méthode (en plaçant des éléments sur la pile), puis vous effectuez une vérification booléenne qui est toujours rapide. Le problème que je vois est la cohérence. Si certaines de vos instructions de débogage et d’information sont encapsulées et d’autres pas, il n’ya pas de style de code cohérent. De plus, une personne pourrait ultérieurement modifier l’instruction de débogage pour inclure la concaténation de chaînes, ce qui est encore assez rapide. J'ai constaté que lorsque nous avons intégré les instructions de débogage et d'information dans une grande application et que nous l'avons profilé, nous avons économisé quelques points de pourcentage en performances. Pas beaucoup, mais assez pour que le travail en vaille la peine. J'ai maintenant quelques macros installées dans IntelliJ pour générer automatiquement des instructions de débogage et d'informations enveloppées pour moi.

0
Javamann

Je recommanderais d'utiliser l'option 2 comme de facto pour la plupart car ce n'est pas très cher.

Cas 1: log.debug ("une chaîne")

Cas2: log.debug ("une chaîne" + "deux chaînes" + object.toString + object2.toString)

Au moment de l'appel de l'un ou de l'autre, le paramètre string dans log.debug (que ce soit le cas 1 ou le cas 2) doit être évalué. C'est ce que tout le monde veut dire par "cher". Si vous avez une condition antérieure, 'isDebugEnabled ()', il n'est pas nécessaire de les évaluer, car c'est là que la performance est enregistrée.

0
Jolly1234

Apache Log4j intègre cette vérification à partir de la version 2.x, il n'est donc plus nécessaire d'avoir isDebugEnabled(). Il suffit de faire debug() et les messages seront supprimés s’ils ne sont pas activés.

0
ha9u63ar