web-dev-qa-db-fra.com

Pourquoi ne pas utiliser Java.util.logging?

Pour la première fois de ma vie, je me trouve dans une position où j'écris une API Java qui sera à source ouverte. J'espère être inclus dans de nombreux autres projets.

Pour la journalisation, j (et les personnes avec qui je travaille) ai toujours utilisé JUL (Java.util.logging) et n’ai jamais eu de problèmes avec cela. Cependant, je dois maintenant comprendre plus en détail ce que je devrais faire pour le développement de mon API. J'ai fait des recherches à ce sujet et, avec les informations dont je dispose, je suis plus confus. D'où ce post.

Depuis que je viens de JUL, je suis partial à ce sujet. Ma connaissance du reste n'est pas si grande.

D'après les recherches que j'ai effectuées, j'ai trouvé ces raisons pour lesquelles les gens n'aiment pas JUL:

  1. "J'ai commencé à développer en Java bien avant la sortie de JUL par Sun et il était tout simplement plus facile pour moi de continuer avec logging-framework-X plutôt que d'apprendre quelque chose de nouveau" . Hmm. Je ne plaisante pas, c'est ce que disent les gens. Avec cet argument, nous pourrions tous faire du COBOL. (Cependant, je peux certainement comprendre que c'est un mec paresseux moi-même)

  2. "Je n'aime pas les noms des niveaux de journalisation dans JUL" . Ok, sérieusement, ce n'est tout simplement pas une raison suffisante pour introduire une nouvelle dépendance.

  3. "Je n'aime pas le format standard de la sortie de JUL" . Hmm. Ceci est juste la configuration. Vous n'avez même pas à faire quoi que ce soit en termes de code. (c’est vrai, jadis, il vous fallait peut-être créer votre propre classe Formatter pour bien faire les choses).

  4. "J'utilise d'autres bibliothèques qui utilisent également logging-framework-X, j'ai donc pensé qu'il était plus simple d'utiliser celle-ci" . Ceci est un argument circulaire, n'est-ce pas? Pourquoi "tout le monde" utilise-t-il logging-framework-X et non JUL?

  5. "Tout le monde utilise logging-framework-X" . Pour moi, c'est juste un cas particulier de ce qui précède. La majorité n'a pas toujours raison.

La vraie grande question est donc pourquoi pas JUL? . Qu'est-ce que j'ai raté? La raison d'être des façades de journalisation (SLF4J, JCL) réside dans le fait que plusieurs implémentations de journalisation existent depuis longtemps et que cela remonte vraiment à l'époque d'avant JUL telle que je la vois. Si JUL était parfait, les façades en bois n'existeraient pas ou quoi? Pour rendre les choses plus confuses, JUL est dans une certaine mesure une façade elle-même, permettant aux gestionnaires, aux formateurs et même au gestionnaire de journaux d'être échangé.

Plutôt que d’adopter de multiples façons de faire la même chose (journalisation), ne devrions-nous pas nous demander pourquoi elles étaient nécessaires au départ? (et voir si ces raisons existent toujours)

Ok, mes recherches jusqu’à présent ont abouti à un certain nombre de choses que je peux voir être de vrais problèmes avec JUL:

  1. Performance . Certains disent que les performances de SLF4J sont supérieures aux autres. Cela me semble être un cas d'optimisation prématurée. Si vous devez vous connecter des centaines de mégaoctets par seconde, je ne suis pas sûr que vous soyez sur le bon chemin. JUL a également évolué et les tests que vous avez effectués sur Java 1.4 ne sont peut-être plus vrais. Vous pouvez lire à ce sujet ici et ce correctif a été transformé en Java 7. Beaucoup parlent également de la surcharge de la concaténation de chaînes dans les méthodes de journalisation. Cependant, la journalisation basée sur des modèles évite ce coût et existe également dans JUL. Personnellement, je n'écris jamais vraiment de journalisation basée sur des modèles Trop paresseux pour ça. Par exemple si je fais cela avec JUL:

    log.finest("Lookup request from username=" + username 
       + ", valueX=" + valueX
       + ", valueY=" + valueY));
    

    mon IDE m'avertira et me demandera la permission de le changer pour:

    log.log(Level.FINEST, "Lookup request from username={0}, valueX={1}, valueY={2}", 
       new Object[]{username, valueX, valueY});
    

    ..qui je vais bien sûr accepter. Permission accordée ! Merci de votre aide.

    Donc, je n’écris pas moi-même de telles déclarations, c’est fait par l’EDI.

    En conclusion sur la question de la performance, je n'ai rien trouvé qui puisse suggérer que la performance de JUL n'est pas satisfaisante par rapport à la concurrence.

  2. Configuration depuis classpath . JUL prêt à l'emploi ne peut pas charger un fichier de configuration à partir du chemin d'accès aux classes. C'est un quelques lignes de code pour le faire. Je peux voir pourquoi cela peut être ennuyeux mais la solution est courte et simple.

  3. Disponibilité des gestionnaires de sortie . JUL est livré avec 5 gestionnaires de sortie prêts à l'emploi: console, flux de fichiers, socket et mémoire. Celles-ci peuvent être étendues ou de nouvelles peuvent être écrites. Cela peut par exemple être écrit dans UNIX/Linux Syslog et Windows Event Log. Personnellement, je n’ai jamais eu cette exigence, ni l’ai vue utilisée, mais je peux certainement comprendre pourquoi elle peut être utile. Logback est livré avec un appender pour Syslog par exemple. Encore je dirais que

    1. 99,5% des besoins en destinations de sortie sont couverts par ce qui est dans JUL out-of-the-box.
    2. Les besoins spéciaux peuvent être pris en charge par les agents de douane personnalisés au-dessus de JUL plutôt que sur autre chose. Rien ne me donne à penser que l'écriture d'un gestionnaire de sortie Syslog pour JUL prend plus de temps que pour un autre cadre de journalisation.

Je suis vraiment préoccupé par quelque chose que j'ai négligé. L'utilisation de façades et d'applications de journalisation autres que JUL est tellement répandue que je dois en venir à la conclusion que c'est moi qui ne comprends tout simplement pas. Ce n'est pas la première fois, j'en ai bien peur. :-)

Alors, que dois-je faire avec mon API? Je veux que ça réussisse. Je peux bien sûr simplement "suivre le courant" et mettre en œuvre SLF4J (qui semble être le plus populaire de nos jours), mais pour ma propre cause, je dois toujours comprendre exactement ce qui ne va pas avec le JUL d’aujourd’hui qui justifie tout le fuzz? Est-ce que je vais me saboter en choisissant JUL pour ma bibliothèque?

Test de performance

(section ajoutée par nolan600 le 07-JUL-2012)

Ceki mentionne ci-dessous que la paramétrisation de SLF4J est 10 fois plus rapide que celle de JUL. J'ai donc commencé à faire des tests simples. À première vue, l’affirmation est certainement correcte. Voici les résultats préliminaires (mais lisez la suite!):

  • Temps d'exécution SLF4J, backend Logback: 1515
  • Temps d'exécution SLF4J, backend JUL: 12938
  • Temps d'exécution JUL: 16911

Les chiffres ci-dessus sont des millisecondes, donc moins c'est mieux. Donc, une différence de performance de 10 fois est d’abord très proche. Ma première réaction: c'est beaucoup!

Voici le coeur du test. Comme on peut le voir, un entier et une chaîne sont construits dans une boucle qui est ensuite utilisée dans l'instruction log:

    for (int i = 0; i < noOfExecutions; i++) {
        for (char x=32; x<88; x++) {
            String someString = Character.toString(x);
            // here we log 
        }
    }

(Je souhaitais que l'instruction de journal contienne à la fois un type de données primitif (dans ce cas un int) et un type de données plus complexe (dans ce cas une chaîne).

L'instruction du journal pour SLF4J:

logger.info("Logging {} and {} ", i, someString);

La déclaration de journal pour JUL:

logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});

La JVM a été "réchauffée" avec le même test exécuté une fois avant que la mesure réelle soit effectuée. Java 1.7.03 a été utilisé sous Windows 7. Les dernières versions de SLF4J (v1.6.6) et de Logback (v1.0.6) ont été utilisées. Stdout et stderr ont été redirigés vers un périphérique nul.

Cependant, attention, JUL passe le plus clair de son temps en getSourceClassName() parce que JUL imprime par défaut le nom de la classe source dans la sortie, contrairement à Logback. Nous comparons donc des pommes et des oranges. Je dois refaire le test et configurer les implémentations de journalisation de manière similaire afin qu'elles produisent le même contenu. Je soupçonne cependant que SLF4J + Logback sera toujours en tête, mais loin des chiffres initiaux indiqués ci-dessus. Restez à l'écoute.

Btw: Le test était la première fois que je travaillais réellement avec SLF4J ou Logback. Une expérience agréable. JUL est certainement beaucoup moins accueillant lorsque vous débutez.

Test de performance (partie 2)

(section ajoutée par nolan600 le 08-JUL-2012)

En fin de compte, la manière dont vous configurez votre modèle dans JUL n'a pas vraiment d'importance en termes de performances, c'est-à-dire qu'il inclut ou non le nom de la source. J'ai essayé avec un motif très simple:

Java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"

et cela n'a pas du tout changé le timing ci-dessus. Mon profileur a révélé que l'enregistreur passait encore beaucoup de temps à appeler getSourceClassName() même si cela ne faisait pas partie de mon modèle. Le motif n'a pas d'importance.

Je conclus donc sur la question des performances qu’au moins pour l’instruction de journal testée basée sur un modèle, il semble y avoir un facteur 10 dans la différence de performance réelle entre JUL (lent) et SLF4J + Logback (rapide). Tout comme Ceki a dit.

Je peux également voir une autre chose, à savoir que l'appel de getLogger() de SLF4J est beaucoup plus coûteux que celui de JUL. (95 ms vs 0,3 ms si mon profileur est précis). C'est logique. SLF4J doit faire un certain temps sur la liaison de l'implémentation de journalisation sous-jacente. Cela ne me fait pas peur. Ces appels devraient être quelque peu rares dans la vie d'une application. La rapidité devrait être dans les appels de journal réels.

Conclusion finale

(section ajoutée par nolan600 le 08-JUL-2012)

Merci pour toutes vos réponses. Contrairement à ce que je pensais au départ, j'ai finalement décidé d'utiliser SLF4J pour mon API. Ceci est basé sur un certain nombre de choses et votre contribution:

  1. Cela permet de choisir la mise en œuvre du journal au moment du déploiement.

  2. Problèmes liés au manque de flexibilité de la configuration de JUL lorsqu'ils sont exécutés dans un serveur d'applications.

  3. SLF4J est certainement beaucoup plus rapide que détaillé ci-dessus, en particulier si vous le couplez à Logback. Même s’il s’agissait là d’un test approximatif, j’ai des raisons de croire que l’optimisation a été beaucoup plus poussée dans SLF4J + Logback que dans JUL.

  4. Documentation. La documentation de SLF4J est simplement plus complète et précise.

  5. Flexibilité du motif. En effectuant les tests, j’ai voulu que JUL mime le modèle par défaut de Logback. Ce modèle comprend le nom du fil. Il s'avère que JUL ne peut pas le faire en dehors de la boîte. D'accord, je ne l'ai pas manqué jusqu'à maintenant, mais je ne pense pas que ce soit une chose qui devrait être absente d'un cadre de journal. Période!

  6. La plupart (ou beaucoup) de projets Java utilisent aujourd'hui Maven. L'ajout d'une dépendance n'est donc pas si important, surtout si cette dépendance est plutôt stable, c'est-à-dire qu'elle ne change pas constamment son API. Cela semble être vrai pour SLF4J. De plus, le pot et les amis du SLF4J sont de petite taille.

La chose étrange qui s’est produite est que j’ai vraiment été très fâché contre JUL après avoir un peu travaillé avec SLF4J. Je regrette toujours qu'il en soit ainsi avec JUL. JUL est loin d'être parfait, mais fait le travail. Juste pas assez bien. La même chose peut être dite à propos de Properties à titre d'exemple, mais nous ne pensons pas à la résumer afin que les gens puissent brancher leur propre bibliothèque de configuration et ce que vous avez. Je pense que la raison en est que Properties arrive juste au-dessus de la barre alors que l'inverse est vrai pour JUL d'aujourd'hui ... et dans le passé, il arrivait à zéro parce qu'il n'existait pas.

320
peterh

Disclaimer : Je suis le fondateur des projets log4j, SLF4J et logback.

Il y a des raisons objectives de préférer SLF4J. D'une part, SLF4J permet à l'utilisateur final de choisir le cadre de journalisation sous-jacent . De plus, les utilisateurs plus avertis ont tendance à préférer logback qui offre des capacités au-delà de log4j , j.u.l étant en retard. En termes de fonctionnalités, j.u.l peut suffire à certains utilisateurs mais pas à d'autres. En un mot, si la journalisation est importante pour vous, vous voudriez utiliser SLF4J avec logback comme implémentation sous-jacente. Si la journalisation est sans importance, j.u.l va bien.

Cependant, en tant que développeur oss, vous devez tenir compte des préférences de vos utilisateurs et pas seulement des vôtres. Il s’ensuit que vous devriez adopter SLF4J non pas parce que vous êtes convaincu que SLF4J est meilleur que jul mais parce que la plupart des développeurs Java actuellement (juillet 2012) préfèrent SLF4J API de journalisation. Si finalement vous décidez de ne pas vous soucier de l'opinion publique, considérez les faits suivants:

  1. ceux qui préfèrent j.u. le font par commodité, car j.u.l est fourni avec le kit JDK. À ma connaissance, il n'y a pas d'autres arguments objectifs en faveur de j.u.l.
  2. votre propre préférence pour j.u.l est juste cela, une préférence .

Par conséquent, tenir les "faits réels" au-dessus de l'opinion publique, bien qu'apparemment courageux, est une erreur logique dans ce cas.

Si toujours pas convaincu, JB Nizet présente un argument supplémentaire puissant:

Sauf que l'utilisateur final aurait peut-être déjà effectué cette personnalisation pour son propre code ou pour une autre bibliothèque utilisant log4j ou logback. j.u.l est extensible, mais devoir étendre la journalisation, j.u.l, log4j et Dieu ne sait que quel autre cadre de journalisation, car il utilise quatre bibliothèques utilisant quatre cadres de journalisation différents, est fastidieux. En utilisant SLF4J, vous lui permettez de configurer les infrastructures de journalisation qu'il souhaite, et non celle que vous avez choisie. N'oubliez pas qu'un projet typique utilise des myriades de bibliothèques, et pas seulement le vôtre .

Si pour quelque raison que ce soit, vous détestez l'API SLF4J et si vous l'utilisez pour supprimer le plaisir de votre travail, optez pour j.u.l. Après tout, il y a moyen de rediriger j.u.l vers SLF4J .

Soit dit en passant, la paramétrisation est au moins 10 fois plus lente que celle de SLF4J, ce qui finit par faire une différence notable.

183
Ceki
  1. Java.util.logging a été introduit dans Java 1.4. Il y avait des utilisations pour la journalisation auparavant, c'est pourquoi de nombreuses autres API de journalisation existent. Ces API étaient fortement utilisées avant Java 1.4 et avaient donc une excellente part de marché qui ne tombait pas à 0 lorsque la version 1.4 était sortie.

  2. JUL n’a pas été très bon au début, beaucoup des choses que vous avez mentionnées étaient bien pires dans la version 1.4 et ne s’est améliorée que dans la version 1.5 (et je suppose aussi en 6, mais je ne suis pas trop sûr).

  3. JUL ne convient pas à plusieurs applications avec différentes configurations dans la même machine virtuelle (pensez à plusieurs applications Web qui ne devraient pas interagir). Tomcat doit franchir quelques étapes pour que cela fonctionne (ré-implémenter JUL efficacement si j'ai bien compris).

  4. Vous ne pouvez pas toujours influencer le cadre de journalisation utilisé par vos bibliothèques. Par conséquent, utiliser SLF4J (qui n'est en réalité qu'une couche très mince d'API au-dessus des autres bibliothèques) permet de garder une image relativement cohérente de tout le monde de la journalisation (vous pouvez donc choisir le cadre de journalisation sous-jacent tout en conservant la journalisation des bibliothèques dans le même système).

  5. Les bibliothèques ne peuvent pas changer facilement. Si une version précédente d'une bibliothèque utilisait autrefois logging-library-X, elle ne peut pas facilement basculer sur logging-library-Y (par exemple, JUL), même si cette dernière est clairement superieuse: tout utilisateur de cette bibliothèque doit apprendre le nouveau cadre de journalisation et (au moins) reconfigurent leur journalisation. C'est un gros non-non, surtout quand cela n'apporte aucun gain apparent à la plupart des gens.

Ayant dit tout ce que je pense, JUL est au moins une alternative valable aux autres cadres de journalisation de nos jours.

29
Joachim Sauer

IMHO, le principal avantage de l'utilisation d'une façade de journalisation telle que slf4j est que vous laissez l'utilisateur final de la bibliothèque choisir quelle implémentation de journalisation concrète il souhaite, plutôt que d'imposer votre choix à l'utilisateur final.

Peut-être a-t-il investi du temps et de l'argent dans Log4j ou LogBack (formateurs spéciaux, ajouts, etc.) et préfère continuer à utiliser Log4j ou LogBack plutôt que de configurer jul. Pas de problème: slf4j le permet. Est-ce un choix judicieux d'utiliser Log4j plutôt que juillet? Peut-être peut-être pas. Mais vous vous en fichez. Laissez l’utilisateur final choisir ce qu’il préfère.

27
JB Nizet

Comme vous le soupçonnez, j’ai commencé à utiliser JUL parce que c’était le moyen le plus facile de commencer immédiatement. Au fil des ans, cependant, je suis venu pour souhaiter avoir passé un peu plus de temps à choisir.

Mon problème principal maintenant est que nous avons une quantité substantielle de code de "bibliothèque" qui est utilisé dans de nombreuses applications et qui utilisent tous JUL. Chaque fois que j'utilise ces outils dans une application de type service Web, la journalisation disparaît ou va quelque part de manière imprévisible ou étrange.

Notre solution a été d’ajouter une façade au code de la bibliothèque, ce qui signifie que les appels du journal de la bibliothèque n’ont pas changé, mais ont été redirigés de manière dynamique vers le mécanisme de journalisation disponible. Lorsqu'ils sont inclus dans un outil POJO, ils sont dirigés vers JUL, mais lorsqu'ils sont déployés en tant qu'application Web, ils sont redirigés vers LogBack.

Nous regrettons - bien sûr - que le code de la bibliothèque n’utilise pas la journalisation paramétrée, mais que celle-ci peut maintenant être modifiée à tout moment.

Nous avons utilisé slf4j pour construire la façade.

6
OldCurmudgeon

J'ai couru jul contre slf4j-1.7.21 sur logback-1.1.7, sortie sur un SSD, Java 1.8, Win64

jul a couru 48449 ms, logback 27185 ms pour une boucle 1M.

Néanmoins, un peu plus de vitesse et une API un peu plus agréable ne vaut pas 3 bibliothèques et 800K pour moi.

package log;

import Java.util.logging.Level;
import Java.util.logging.Logger;

public class LogJUL
{
    final static Logger logger = Logger.getLogger(LogJUL.class.getSimpleName());

    public static void main(String[] args) 
    {
        int N = 1024*1024;

        long l = System.currentTimeMillis();

        for (int i = 0; i < N; i++)
        {
            Long lc = System.currentTimeMillis();

            Object[] o = { lc };

            logger.log(Level.INFO,"Epoch time {0}", o);
        }

        l = System.currentTimeMillis() - l;

        System.out.printf("time (ms) %d%n", l);
    }
}

et

package log;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogSLF
{
    static Logger logger = LoggerFactory.getLogger(LogSLF.class);


    public static void main(String[] args) 
    {
        int N = 1024*1024;

        long l = System.currentTimeMillis();

        for (int i = 0; i < N; i++)
        {
            Long lc = System.currentTimeMillis();

            logger.info("Epoch time {}", lc);
        }

        l = System.currentTimeMillis() - l;

        System.out.printf("time (ms) %d%n", l);
    }

}
2
weberjn