Comment améliorer le mécanisme de journalisation avec Java8s lambdas
Comment est-il possible, pour améliorer votre mécanisme de journalisation, en n'ayant pas la surcharge de concaténations de chaîne?
Considérons l'exemple suivant:
import java.util.logging.Level;
import java.util.logging.Logger;
public class LoggerTest {
public static void main(String[] args) {
//get logger
Logger log = Logger.getLogger(LoggerTest.class.getName());
//set log level to INFO (so fine will not be logged)
log.setLevel(Level.INFO);
//this line won't log anything, but will evaluate the getValue method
log.fine("Trace value: " + getValue());
}
//example method to get a value with a lot of string concatenation
private static String getValue() {
String val = "";
for (int i = 0; i < 1000; i++) {
val += "foo";
}
return val;
}
}
La méthode log log.fine(...)
sera pas enregistrer quoi que ce soit, parce que le niveau de journalisation est défini à INFO
. Le problème, c'est que la méthode getValue
seront évalués de toute façon.
Et c'est un gros problème de performances dans les grandes applications avec beaucoup d'instructions de débogage.
Alors, comment résoudre ce problème?
Utiliser les slf4j?
Merci pour l'information! Mais, pensez à vérifier si nous sommes en train de dire
oui en effet je pensais que vous étiez juste la concaténation de deux chaînes - je n'avais pas réalisé que c'était de 1000 chaînes!
Dire aussi utiliser slf4j. Pour les 99% de l'exploitation forestière, il va faire de la sémantique 'enregistreur.debug("Regarde-moi maman: {}",me)' . Mais bien sûr, si vous savez que vous allez à la concaténation d'une chaîne d'un millier de fois, puis l'envelopper dans un si(logger.isDebug()).. . Les lambdas est sympa mais je ne suis pas sûr que ça ajoute vraiment quelque chose d'utile et plus efficace.
Pourquoi ne pas simplement utiliser java.util.logging.Logger.info(Fournisseur<String>) et être fait avec elle une fois pour toutes?
Merci pour l'information! Mais, pensez à vérifier si nous sommes en train de dire
log.fine("Trace value: {}", getValue());
il évalue également les getValue
méthode dans tous les cas, n'est-ce pas?oui en effet je pensais que vous étiez juste la concaténation de deux chaînes - je n'avais pas réalisé que c'était de 1000 chaînes!
Dire aussi utiliser slf4j. Pour les 99% de l'exploitation forestière, il va faire de la sémantique 'enregistreur.debug("Regarde-moi maman: {}",me)' . Mais bien sûr, si vous savez que vous allez à la concaténation d'une chaîne d'un millier de fois, puis l'envelopper dans un si(logger.isDebug()).. . Les lambdas est sympa mais je ne suis pas sûr que ça ajoute vraiment quelque chose d'utile et plus efficace.
Pourquoi ne pas simplement utiliser java.util.logging.Logger.info(Fournisseur<String>) et être fait avec elle une fois pour toutes?
OriginalL'auteur bobbel | 2014-02-13
Vous devez vous connecter pour publier un commentaire.
Depuis Java8 il est possible d'utiliser le nouveau introduit les expressions lambda pour ce scénario.
Ici est une version modifiée de l'exemple de l'exploitation forestière:
LoggerTest.class
LambdaLogger.class
Avec cette modification, vous pouvez améliorer les performances de vos applications beaucoup, si vous avez de nombreux journaux déclarations, qui sont seulement à des fins de débogage.
Bien sûr, vous pouvez utiliser n'importe quel Enregistreur que vous souhaitez. Ce n'est qu'un exemple de la
java.util.Logger
.Callable.call
. Ce peut-être mieux que la concaténation de chaîne, mais toujours susceptible d'être plus cher que l'emballage de lafine
appel à un si déclaration qui vérifie si le niveau est activé. Goetz traite de la capture de coûts ici.Un bon point de départ pour la discussion de la capture de coûts dans la vidéo (en ignorant toutes les informations de fond) serait d'environ [36:25][1]. [1]: youtu.être/C_QbkGU_lqY?t=36m25s
Il n'est pas nécessaire d'étendre l'Enregistreur de classe et de mettre en œuvre des méthodes personnalisées depuis Java 8 a déjà enregistreur de méthodes similaires signature:
public void fine(Supplier<String> msgSupplier)
.OriginalL'auteur bobbel
@bobbel a expliqué comment le faire.
Je tiens à ajouter que, bien que cela représente une amélioration de la performance au-dessus de votre code d'origine, la façon classique de faire face à ce qui est encore plus rapide:
et un peu plus détaillé /verbeux.
La raison pour laquelle il est plus rapide, c'est que le lambda version a le runtime frais généraux de la création de l'instance appelable (capture de coût), et un niveau supplémentaire d'appels de méthode.
Et enfin, il y a la question de la création de la
LambdaLogger
instances. @bobbel code montre ce que cela soit fait à l'aide d'un constructeur, mais en réalitéjava.util.logging.Logger
objets doivent être créés par une usine méthode pour éviter la prolifération d'objets. Cela implique un tas d'infrastructure supplémentaire (et les changements de code) pour obtenir que cela fonctionne avec une sous-classe personnalisée deLogger
.if
est toujours le meilleur.Bien sûr, cela est également possible (et peut-être même plus rapide)! Mais, imaginez que vous avez à écrire ce supplément de
if
pour chaque instruction de journal que vous avez. Donc, si vous n'avez pas beaucoup de journaux-états, cette option doit être préférée!Désolé. Non, je ne préfère pas. Je n'accepte pas qu'il est nécessaire de "garde" la plupart de journalisation des appels, ou que vous devriez normalement avoir que beaucoup de "beaux" enregistrement des appels dans votre code dans la première place.
Un autre point est de garder les niveaux de la
if
de la garde et le journal réel de l'instruction de synchronisation.Vrai. Mais les deux morceaux de code sont généralement sur des lignes consécutives, et les deux niveau de journalisation symboles sont en majuscules. Le programmeur doit être vraiment imprudent pour eux de sortir de la synchronisation.
OriginalL'auteur Stephen C
Apparemment Log4j 2.4 inclut le support pour les lambda expressions qui sont exactement utiles pour votre cas (et qui d'autres réponses que nous avons reproduits manuellement):
De https://garygregory.wordpress.com/2015/09/16/a-gentle-introduction-to-the-log4j-api-and-lambda-basics/
OriginalL'auteur jhyot
Il suffit de créer les méthodes de wrapper pour votre journal comme:
et de l'utiliser:
Référence: JAVA SE 8 pour la Vraiment Impatient eBook, pages 48-49.
Car, @KlitosKyriacou, vous ne pouviez pas changer de sauvegarde, par exemple, à log4j, simplement en laissant tomber un JAR dans le classpath.
OriginalL'auteur mauretto
utiliser un format
String
, et un tableau deSupplier<String>
. de cette façon aucunetoString
méthodes sont appelées à moins que le l'enregistrement du journal est en fait publiable. de cette façon, vous n'avez pas à s'embêter avec laideif
déclarations sur l'exploitation forestière dans le code de l'application.getValue()
méthode. Si vous mettez cette méthode comme un argument de type Chaîne, il va évaluer, en tout cas. Ce n'est donc pas une solution efficace.thats fine, im suggérant de modifier l'enregistreur appel à
log.fine("%s", this::getValue)
. nécessite que vous roulez votre propre enregistreur de classe.Ah ok, j'ai mal compris votre réponse. Alors c'est super! Vous pouvez étendre votre réponse avec cette explication. Maintenant, je pense à propos de la différence entre
Supplier.get()
etCallable.call()
...À mon avis, le Fournisseur est mieux quand vous avez à réaliser certaines des opérations gourmandes en ressources d'attente de la chaîne résultante.
OriginalL'auteur aepurniet