web-dev-qa-db-fra.com

Formatage de chaîne intégré vs concaténation de chaîne en tant que paramètre de journalisation

J'utilise SonarLint qui me montre un problème dans la ligne suivante.

LOGGER.debug("Comparing objects: " + object1 + " and " + object2);

Note: la méthode qui contient cette ligne peut être appelée assez souvent.

La description de ce problème est

Les "conditions préalables" et les arguments de journalisation ne devraient pas nécessiter d'évaluation (squid: S2629)

La transmission d'arguments de message nécessitant une évaluation plus approfondie dans une vérification com.google.common.base.Preconditions de Guava peut entraîner une baisse des performances. En effet, qu'ils soient nécessaires ou non, chaque argument doit être résolu avant que la méthode ne soit réellement appelée.

De même, le passage de chaînes concaténées dans une méthode de journalisation peut également entraîner un impact inutile sur les performances car la concaténation sera effectuée à chaque appel de la méthode, que le niveau de journalisation soit suffisamment bas ou non pour afficher le message.

Au lieu de cela, vous devez structurer votre code pour passer des valeurs statiques ou précalculées dans la vérification des conditions de préconditions et la journalisation des appels.

Plus précisément, le formatage de chaîne intégré doit être utilisé à la place de la concaténation de chaîne, et si le message est le résultat d'un appel de méthode, les conditions préalables doivent être ignorées et l'exception appropriée doit être levée conditionnellement à la place.

Exemple de code non conforme

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

Solution conforme

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()); }

Je ne suis pas sûr à 100% si je comprends bien. Alors, pourquoi est-ce vraiment un problème. Surtout la partie concernant les performances atteintes lors de l'utilisation de la concaténation de chaînes. Parce que je lis souvent que la concaténation de chaînes est plus rapide que la mise en forme.

EDIT: Peut-être que quelqu'un peut m'expliquer la différence entre

LOGGER.debug("Comparing objects: " + object1 + " and " + object2);

ET

LOGGEr.debug("Comparing objects: {} and {}",object1, object2);

est en arrière-plan. Parce que je pense que la chaîne sera créée avant d'être transmise à la méthode. Droite? Donc pour moi, il n'y a pas de différence. Mais évidemment, je me trompe parce que SonarLint s'en plaint

15
Naxos84

Je pense que vous avez là votre réponse.

La concaténation est calculée au préalable le contrôle d'état. Donc, si vous appelez votre infrastructure de journalisation 10 000 fois de manière conditionnelle et que toutes les valeurs sont fausses, vous concaténerez 10 000 fois sans raison.

Vérifiez également cette rubrique . Et vérifiez les commentaires de la réponse d'Icaro.

Jetez un œil à StringBuilder aussi.

11
luso

Tenez compte de l'instruction de journalisation ci-dessous:

LOGGER.debug("Comparing objects: " + object1 + " and " + object2);

quel est ce "débogage"?

Il s'agit du niveau de l'instruction de journalisation et non du niveau du LOGGER. Vous voyez, il y a 2 niveaux:

a) l'une des instructions de journalisation (qui est déboguée ici):

"Comparing objects: " + object1 + " and " + object2

b) L'un est le niveau du LOGGER. Alors, quel est le niveau de l'objet LOGGER: Cela doit également être défini dans le code ou dans certains xml, sinon il prend le niveau de son ancêtre.

Maintenant, pourquoi dis-je tout cela?

Désormais, la déclaration de journalisation sera imprimée (ou, en termes plus techniques, envoyée à son "appender") si et seulement si:

Level of logging statement >= Level of LOGGER defined/obtained from somewhere in the code

Les valeurs possibles d'un niveau peuvent être

DEBUG < INFO <  WARN < ERROR

(Il peut y en avoir quelques autres selon le cadre de journalisation)

Revenons maintenant à la question:

"Comparing objects: " + object1 + " and " + object2

conduira toujours à la création d'une chaîne même si nous constatons que la "règle de niveau" expliquée ci-dessus échoue.

Cependant,

LOGGER.debug("Comparing objects: {} and {}",object1, object2);

n'entraînera la formation de chaînes que si la "règle de niveau expliquée ci-dessus" est satisfaite.

Alors, quel est le plus intelligent?

Consultez ceci rl .

5
Breaking Benjamin

La concaténation de chaînes signifie LOGGER.info ("Le programme a commencé à" + new Date ());

Formatage intégré des moyens d'enregistrement
LOGGER.info ("Le programme a commencé à {}", nouveau Date ());

très bon article pour comprendre la différence http://dba-present.com/index.php/jvm/Java/120-use-the-built-in-formatting-to-construct-this-argument =

4
Shubham Pandey