Tutoriel pour logguer facilement, en Java à l'aide de Flogger, en 5 minutes

Thierry-Leriche-Dessirier

Flogger est une API de log fluente pour Java. Flogger ajoute un panel de fonctionnalités intéressantes aux bibliothèques de log habituelles, notamment dans la façon de l’utiliser. Et ses concepteurs promettent que les bénéfices sont substantiels.

Dans cet article rapide, nous allons découvrir Flogger et voir comment cette nouvelle bibliothèque se positionne sur le marché. Et pour cela, on se donne 5 minutes… Commentez

- Titre : Tutoriel pour logguer facilement, en Java à l'aide de Flogger, en 5 minutes

- Auteur : Thierry-Leriche-Dessirier

- Parution : 18 août 2019

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2019 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Premiers pas

- II-A. Installation

- II-B. Avec Log4j

- II-C. Avec Java Logging

- II-D. Avec Flogger

- III. Flogger plus en détail

- III-A. Niveaux fluides

- III-B. Printf

- III-C. Lazy

- III-D. Exceptions

- IV. Conclusion

- V. Remerciements

- VI. Annexes

- VI-A. Liens

- VI-B. Code complet de l'article

I - Introduction

Oui… Encore une bibliothèque de log !… Et ce n’est pas ce qui manque sur le marché.

La plupart des équipes Java ont l’habitude d’utiliser Log4j ou des outils dérivés. Si vous utilisez Spring Boot, vous êtes très certainement dans ce cas. Et si vous utilisez déjà Log4j et que vous en êtes pleinement satisfait, vous n’aurez probablement pas besoin d’aller voir la concurrence.

L’API interne de Java (Java Logging API), quant à elle, est arrivée tardivement, mais a déjà fait évoluer les pratiques. Flogger ressemble d’ailleurs plus à l’API Java qu’à Log4j.

Flogger est développée par Google, comme Guava par exemple, et est utilisée dans de nombreux projets internes. Ce point, à lui seul, justifie de jeter un œil à la bibliothèque.

Flogger est proposée sous licence Apache 2.0. Le code de la bibliothèque est disponible sous Github.

Google veut proposer une API de log unifiée, à minima pour ses propres projets. C’est très ambitieux. La firme annonce avoir corrigé de nombreux bogues grâce à Flogger, ce qui est en soi surprenant et inquiétant. Google multiplie les annonces d’autopromotion sur la performance, la qualité ou encore l’adoption. Le temps dira si elles sont justifiées.

Comme nous le verrons plus loin, Flogger propose une API Fluide. Remarquez que le nom de la bibliothèque peut se lire Fluent Logger. Les logs vont donc s’écrire de manière fluide, ce qui rendra le code plus lisible, en particulier lorsque les paramètres se multiplient. Le format (printf) des logs contribue également à améliorer la lisibilité.

Nous verrons également que Flogger se positionne au niveau des performances, notamment grâce à l’utilisation de lasy ou des lambdas. Toutefois, si vous utilisiez Log4j proprement, vous devriez déjà avoir un niveau de performance acceptable pour la plupart des projets. Cet aspect ne devrait donc pas vous préoccuper outre mesure, mis à part que c’est toujours bon à prendre.

Et ce sera déjà bien. Fixons-nous pour objectif de voir tout cela en 5 minutes chrono…

II - Premiers pas

II-A - Installation

Comme souvent, le plus simple va être de passer par Maven, Gradle, etc. Dans cet article, nous allons utiliser Maven, mais sentez-vous libre d’utiliser votre système préféré.

Rendez-vous sur le Central Repository dédié à Flogger pour récupérer la configuration pour votre gestionnaire : https://search.maven.org/artifact/com.google.flogger/flogger

Nous allons donc ajouter une dépendance à Flogger dans le fichier pom.xml. Et puisque, dans cet article, nous souhaitons comparer la bibliothèque avec Log4j, nous allons également ajouter une dépendance vers cette dernière. Enfin, l’API interne (Java Logging API) faisant déjà partie des packages Java, elle ne nécessitera pas de dépendance supplémentaire.

pom.xml

1. <properties>

2. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

3. <maven.compiler.source>1.8</maven.compiler.source>

4. <maven.compiler.target>1.8</maven.compiler.target>

6. <log4j.version>1.2.17</log4j.version>

7. <flogger.version>0.4</flogger.version>

8. <junit.version>4.12</junit.version>

9. </properties>

11. <dependencies>

12. <!-- Log4j -->

13. <dependency>

14. <groupId>log4j</groupId>

15. <artifactId>log4j</artifactId>

16. <version>${log4j.version}</version>

17. </dependency>

19. <!-- Flogger -->

20. <dependency>

21. <groupId>com.google.flogger</groupId>

22. <artifactId>flogger</artifactId>

23. <version>${flogger.version}</version>

24. </dependency>

25. <dependency>

26. <groupId>com.google.flogger</groupId>

27. <artifactId>flogger-system-backend</artifactId>

28. <version>${flogger.version}</version>

29. </dependency>

31. ...

33. </dependencies>

On ajoute également une dépendance vers flogger-system-backend qui va réellement effectuer le travail de l’API. Flogger peut déléguer les traitements à des bibliothèques tierces, y compris à Log4j et Java API…

II-B - Avec Log4j

Doit-on encore présenter Log4j ? Je pense que non. Mais si vous avez besoin d’un petit rappel, je vous recommande l’excellent article de Sébastien Le Ray, intitulé tout simplement « Introduction à Log4j ».

Dans les grandes lignes, vous avez besoin d’un fichier de configuration, suivant le formalisme qui aura votre préférence, comme log4j.properties, notamment pour spécifier le niveau de log. Voici un exemple indiquant qu’on sera en DEBUG dans la console.

log4j.properties

1. log4j.rootLogger=DEBUG, stdout

3. log4j.appender.stdout=org.apache.log4j.ConsoleAppender

4. log4j.appender.stdout.Target=System.out

5. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

6. log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p [%c{1}:%L] - %m%n

Log4j propose cinq priorités qui possèdent un ordre hiérarchique croissant : DEBUG, INFO, WARN, ERROR et FATAL.

Habituellement, on définit un loggeur en début de classe et on l'utilise dans les méthodes.

Main.java

1. public class Main {

2. private static final org.apache.log4j.Logger LOG4J_LOGGER = org.apache.log4j.Logger.getLogger(Main.class.getName());

4. public static void main(String[] args) {

5. System.out.println("Hello +++ ALL +++ (sysout)...");

7. // Log4j

8. LOG4J_LOGGER.debug("Hello *** DEBUG *** (log4j)...");

9. LOG4J_LOGGER.info("Hello *** INFO *** (log4j)...");

10. LOG4J_LOGGER.error("Hello *** ERROR *** (log4j)...");

11. ...

Partons du principe que vous êtes déjà familier avec les stratégies de log et épargnons-nous le débat lié à System.out.println.

L’exécution du programme va écrire dans la console.

Console

Hello +++ ALL +++ (sysout)...
2019-08-20 15:29:20 DEBUG [Main:16] - Hello *** DEBUG *** (log4j)...
2019-08-20 15:29:20 INFO  [Main:17] - Hello *** INFO *** (log4j)...
2019-08-20 15:29:20 ERROR [Main:18] - Hello *** ERROR *** (log4j)...

II-C - Avec Java Logging

L’API Java est très proche de celle de Log4j. Vous ne devriez pas être perdu même si quelques petites différences, notamment dans les formats et les niveaux, peuvent rendre votre démarrage laborieux.

Vous pouvez surcharger la configuration par défaut dans un fichier, que vous pouvez soit inclure dans votre path, soit spécifier au lancement de l’application. Cette seconde option est d’ailleurs conseillée.

my-logging.properties

1. handlers = java.util.logging.ConsoleHandler

2. .level = FINE

3. java.util.logging.ConsoleHandler.level = FINE

4. java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

5. java.util.logging.SimpleFormatter.format = %1$tF %1$tT %4$-7s [%2$s] - %5$s %n

Java Logging propose sept priorités qui possèdent un ordre hiérarchique croissant : FINEST, FINER, FINE, CONFIG, INFO, WARNING et SEVERE. Les trois premiers niveaux (FINEST, FINER et FINE) correspondent globalement au niveau DEBUG de Log4j, ce qui est pratique. En revanche le dernier niveau (SEVERE) remplace les niveaux ERROR et FATAL de Log4j, ce qui est déroutant au départ.

Note : pour indiquer le fichier de configuration à utiliser, il suffit d'ajouter un argument pour votre JVM .

Argument

-Djava.util.logging.config.file=my-logging.properties

Comme avec Log4j, on définit un loggeur en début de classe et on l'utilise dans les méthodes.

Main.java

1. public class Main {

2. private static final org.apache.log4j.Logger LOG4J_LOGGER = org.apache.log4j.Logger.getLogger(Main.class.getName());

3. private static final java.util.logging.Logger JDK_LOGGER = java.util.logging.Logger.getLogger(Main.class.getName());

5. public static void main(String[] args) {

6. System.out.println("Hello +++ ALL +++ (sysout)...");

8. // Log4j

9. LOG4J_LOGGER.debug("Hello *** DEBUG *** (log4j)...");

10. LOG4J_LOGGER.info("Hello *** INFO *** (log4j)...");

11. LOG4J_LOGGER.error("Hello *** ERROR *** (log4j)...");

13. // JDK

14. JDK_LOGGER.fine("Hello ### FINE ### (jdk)...");

15. JDK_LOGGER.info("Hello ### INFO ### (jdk)...");

16. JDK_LOGGER.severe("Hello ### SEVERE ### (jdk)...");

17. ...

Ici on a conservé les lignes correspondant à Log4j pour mettre en évidence les différences.

L’exécution du programme va écrire dans la console.

Console

Hello +++ ALL +++ (sysout)...
2019-08-20 16:42:23 DEBUG [Main:16] - Hello *** DEBUG *** (log4j)...
2019-08-20 16:42:23 INFO  [Main:17] - Hello *** INFO *** (log4j)...
2019-08-20 16:42:23 ERROR [Main:18] - Hello *** ERROR *** (log4j)...
2019-08-20 16:42:23 PRÉCIS  [com.ice.article.flogger.Main main] - Hello ### FINE ### (jdk)...
2019-08-20 16:42:23 INFOS   [com.ice.article.flogger.Main main] - Hello ### INFO ### (jdk)...
2019-08-20 16:42:23 GRAVE   [com.ice.article.flogger.Main main] - Hello ### SEVERE ### (jdk)...

Mis à part quelques subtilités, les fonctionnements et les utilisations de Log4j et de Java Logging sont globalement les mêmes.

II-D - Avec Flogger

Flogger est véritablement proche de Java Logging. Vous pouvez tout simplement réutiliser le fichier de configuration de Java Logging. Il fonctionnera pour les deux.

C’est au niveau du code que les différences vont apparaître. Les appels aux méthodes fine, info ou severe vont être remplacés par des appels fluides. À ce stade, on constate que c’est légèrement plus lisible.

Main.java

1. import com.google.common.flogger.FluentLogger;

3. public class Main {

4. private static final org.apache.log4j.Logger LOG4J_LOGGER = org.apache.log4j.Logger.getLogger(Main.class.getName());

5. private static final java.util.logging.Logger JDK_LOGGER = java.util.logging.Logger.getLogger(Main.class.getName());

6. private static final FluentLogger FLUENT_LOGGER = FluentLogger.forEnclosingClass();

8. public static void main(String[] args) {

9. System.out.println("Hello +++ ALL +++ (sysout)...");

11. // Log4j

12. LOG4J_LOGGER.debug("Hello *** DEBUG *** (log4j)...");

13. LOG4J_LOGGER.info("Hello *** INFO *** (log4j)...");

14. LOG4J_LOGGER.error("Hello *** ERROR *** (log4j)...");

16. // JDK

17. JDK_LOGGER.fine("Hello ### FINE ### (jdk)...");

18. JDK_LOGGER.info("Hello ### INFO ### (jdk)...");

19. JDK_LOGGER.severe("Hello ### SEVERE ### (jdk)...");

21. // Flogger

22. FLUENT_LOGGER.atFine().log("Hello --- FINE --- (Flogger)...");

23. FLUENT_LOGGER.atInfo().log("Hello --- INFO --- (Flogger)...");

24. FLUENT_LOGGER.atSevere().log("Hello --- SEVERE --- (Flogger)...");

26. ...

Sans surprise, à l’exécution du programme, on ne voit pas de différence entre Flogger et Java Logging.

Console

Hello +++ ALL +++ (sysout)...
2019-08-20 16:42:23 DEBUG [Main:16] - Hello *** DEBUG *** (log4j)...
2019-08-20 16:42:23 INFO  [Main:17] - Hello *** INFO *** (log4j)...
2019-08-20 16:42:23 ERROR [Main:18] - Hello *** ERROR *** (log4j)...
2019-08-20 16:42:23 PRÉCIS  [com.ice.article.flogger.Main main] - Hello ### FINE ### (jdk)...
2019-08-20 16:42:23 INFOS   [com.ice.article.flogger.Main main] - Hello ### INFO ### (jdk)...
2019-08-20 16:42:23 GRAVE   [com.ice.article.flogger.Main main] - Hello ### SEVERE ### (jdk)...
2019-08-20 16:42:23 PRÉCIS  [com.ice.article.flogger.Main main] - Hello --- FINE --- (Flogger)...
2019-08-20 16:42:23 INFOS   [com.ice.article.flogger.Main main] - Hello --- INFO --- (Flogger)...
2019-08-20 16:42:23 GRAVE   [com.ice.article.flogger.Main main] - Hello --- SEVERE --- (Flogger)...

III - Flogger plus en détail

III-A - Niveaux fluides

Flogger est très proche de Java Logging. Sans surprise, les possibilités vont se ressembler, à commencer par la possibilité de préciser le niveau de log. Et c’est là que Flogger marque des points grâce à son API fluide, là où Java API multiplie les paramètres dans sa méthode de log.

Main.java

1. final Level level = Level.INFO;

2. JDK_LOGGER.log(level, "Hello amigo (jdk) !!!");

3. FLUENT_LOGGER.at(level)

4. .log("Hello amigo (Flogger) !!!");

Cela se traduit naturellement par un byte code plus simple et plus efficace.

À l’exécution, on ne constate toutefois aucune différence.

Console

2019-08-20 16:42:23 INFOS   [com.ice.article.flogger.Main main] - Hello amigo (jdk) !!!
2019-08-20 16:42:23 INFOS   [com.ice.article.flogger.Main main] - Hello amigo (Flogger) !!!

III-B - Printf

Flogger utilise de base le format printf sans devoir passer par des classes intermédiaires. Ce n’est évidemment pas révolutionnaire, puisque c’est faisable en Java pur, mais ça simplifie l’écriture et rend le code plus lisible.

Main.java

1. final Level level = Level.INFO;

2. final String name = "John";

3. FLUENT_LOGGER.at(level)

4. .log("My name is " + name + " and I love you (Flogger)");

5. FLUENT_LOGGER.at(level)

6. .log("My name is %s and I still love you (Flogger)", name);

À l’exécution, on ne constate toutefois aucune différence.

Console

2019-08-20 17:14:15 INFOS   [com.ice.article.flogger.Main main] - My name is John and I love you (Flogger)
2019-08-20 17:14:15 INFOS   [com.ice.article.flogger.Main main] - My name is John and I still love you (Flogger)

Ça n’a l’air de rien, mais c’est beaucoup plus simple et clair d’utiliser le formalisme de printf plutôt que de faire des concaténations. D’ailleurs, lors de l’écriture de cet article, je m’y suis repris à plusieurs fois, car j’avais naturellement oublié de mettre des espaces aux bons endroits dans la version concaténée.

Accessoirement, ou non, l’utilisation d’un pattern est bien plus performante qu’une concaténation, en particulier lorsque le niveau de log n’est pas atteint. Cela évite de nombreux (statistiquement) traitements inutiles.

Bien entendu, comme avec Log4j, il est possible d’encapsuler le log dans une condition (if) mais la lisibilité du code en prend un coup. Pour bien faire, il aurait fallu écrire le code comme suit, et on se rend compte que ça devient lourd :

Main.java

1. final Level level = Level.INFO;

2. final String name = "John";

4. // Concat

5. if (FLUENT_LOGGER.at(level).isEnabled()) {

6. FLUENT_LOGGER.at(level)

7. .log("My name is " + name + " and I love you (Flogger)");

8. }

10. // printf

11. FLUENT_LOGGER.at(level)

12. .log("My name is %s and I still love you (Flogger)", name);

De manière générale, dans sa page dédiée aux bonnes pratiques, l’équipe Flogger conseille de logguer des objets directement (format printf) et de ne surtout pas faire appel à la représentation sous forme de string dudit objet.

Ainsi, et comme déjà expliqué ci-dessus, il est déconseillé d’écrire du code qui transforme l’objet en string, comme dans le bloc suivant.

Main.java

1. FLUENT_LOGGER.atFine()

2. .log("My complete name is %s and I am not your father :-p", person.toString());

3. FLUENT_LOGGER.atFine()

4. .log("My complete name is " + person + " and I am not your father :-p");

Ici, la concaténation de person génère un appel implicite à person.toString, même si le log n'est finalement pas effectué.

Il vaut mieux passer directement l’objet en ellipse (varargs) sans faire appel à toString et c’est la bibliothèque qui va gérer.

Main.java

1. FLUENT_LOGGER.atFine()

2. .log("My complete name is %s and I am not your father :-p", person);

III-C - Lazy

Dans certains cas, on est tenté de faire des appels supplémentaires à l’occasion d’un log, même si c’est déconseillé.

Main.java

1. final String name = "John";

2. FLUENT_LOGGER.atFine()

3. .log("I am %s and I like logs...", doSomethingVeryLong(name));

En première approche, on pourrait penser à encapsuler le log (et son traitement très long) dans une condition, comme on l’a vu dans la section précédente, mais il est plus intéressant de combiner lazy et une lambda.

Main.java

import static com.google.common.flogger.LazyArgs.lazy;

FLUENT_LOGGER.atFine()
.log("I am %s and I like lazy logs...", lazy(() -> doSomethingVeryLong(name)));

Au passage, on y gagne non seulement en performance, mais aussi en lisibilité.

III-D - Exceptions

C’est toujours un casse-tête de logguer des exceptions. Flogger propose quelque chose d’intéressant. Partons du code suivant :

Main.java

1. try {

2. ...

3. throw new FileNotFoundException("The file foo.txt was not found.");

4. } catch (final FileNotFoundException e) {

5. FLUENT_LOGGER.atInfo()

6. .withCause(e)

7. .log("I am %s and I am not happy!", name);

8. }

L’exécution de ce bloc de code va masquer l’exception, ce qui n’est pas forcément ce qu’on souhaite.

Console

2019-08-20 18:05:25 INFOS   [com.ice.article.flogger.Main main] - I am John and I am not happy!

Ce qu’il faut comprendre, c’est que le résultat du log est décidé en fonction de la configuration, et non plus en fonction du code à proprement parler. Pour le mettre en évidence, modifions my-logging.properties pour revenir au format par défaut.

my-logging.properties

1. handlers = java.util.logging.ConsoleHandler

2. .level = FINE

3. java.util.logging.ConsoleHandler.level = FINE

4. java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

5. #java.util.logging.SimpleFormatter.format = %1$tF %1$tT %4$-7s [%2$s] - %5$s %n

Le format de log n’a plus rien à voir. Par défaut, il utilise deux lignes, ce que je trouvais moins lisible pour cet article, en particulier dans le cadre d’une comparaison avec Log4j. En revenant au format par défaut, on active du logging des détails des éventuelles exceptions.

Console

août 20, 2019 6:13:32 PM com.ice.article.flogger.Main main
INFOS: I am John and I am not happy!
java.io.FileNotFoundException: The file foo.txt was not found.
at com.ice.article.flogger.Main.main(Main.java:58)

Je vous laisse mesurer à quel point ce sera pratique sur votre serveur en production…

IV - Conclusion

Aurons-nous réussi à présenter l’essentiel de Flogger en 5 minutes seulement ? On ne doit pas en être loin. Comme vous vous en doutez, la bibliothèque est bien plus riche que cette rapide présentation le laisse entrevoir. Je vous encourage donc à jeter un œil à la documentation officielle, qui s’enrichit progressivement.

Nous avons fait l’impasse sur les possibilités d’extensions de Flogger ainsi que sur ses possibles interactions avec les autres bibliothèques du marché. N’hésitez pas à aborder le sujet dans les commentaires si cela vous intéresse.

Comme vous l’avez certainement remarqué, la version de Flogger utilisée dans cet article est la version 0.4. La bibliothèque n'a pas encore de release 1.0. L’API a déjà bien évolué depuis son lancement et continue sur sa lancée. Certaines fonctionnalités mentionnées dans les tout premiers articles dédiés à Flogger, comme atError, ont tout bonnement été supprimées tandis que d’autres sont apparues. Et il est probable que cet article soit lui-même en partie périmé d’ici quelque temps. Là encore, n’hésitez pas à en discuter dans les commentaires.

Vos retours et remarques nous aident à améliorer les publications de Developpez.com. N'hésitez donc pas à commenter cet article. Commentez

V - Remerciements

D'abord j'adresse mes remerciements à l'équipe Flogger, chez Google, pour avoir développé cette bibliothèque et pour la maintenir. Je n'oublie pas tous les contributeurs qui participent, notamment sur le forum Flogger.

Plus spécifiquement en ce qui concerne cet article, je tiens à remercier l'équipe de Developpez.com et plus particulièrement Claude LELOUP.

VI - Annexes

VI-A - Liens

Documentation de Flogger : https://google.github.io/flogger

Github de Flogger : https://github.com/google/flogger

Central Repository dédié à Flogger : https://search.maven.org/artifact/com.google.flogger/flogger

Mes autres articles de la série en 5 minutes : https://thierry-leriche-dessirier.developpez.com/#5min

VI-B - Code complet de l'article

Main.java

1. package com.ice.article.flogger;

3. import static com.google.common.flogger.LazyArgs.lazy;

5. import java.io.FileNotFoundException;

6. import java.util.logging.Level;

8. import com.google.common.flogger.FluentLogger;

10. public class Main {

12. private static final org.apache.log4j.Logger LOG4J_LOGGER = org.apache.log4j.Logger.getLogger(Main.class.getName());

13. private static final java.util.logging.Logger JDK_LOGGER = java.util.logging.Logger.getLogger(Main.class.getName());

14. private static final FluentLogger FLUENT_LOGGER = FluentLogger.forEnclosingClass();

16. public static void main(String[] args) {

17. System.out.println("Hello +++ ALL +++ (sysout)...");

19. LOG4J_LOGGER.debug("Hello *** DEBUG *** (log4j)...");

20. LOG4J_LOGGER.info("Hello *** INFO *** (log4j)...");

21. LOG4J_LOGGER.error("Hello *** ERROR *** (log4j)...");

23. JDK_LOGGER.fine("Hello ### FINE ### (jdk)...");

24. JDK_LOGGER.info("Hello ### INFO ### (jdk)...");

25. JDK_LOGGER.severe("Hello ### SEVERE ### (jdk)...");

27. FLUENT_LOGGER.atFine().log("Hello --- FINE --- (Flogger)...");

28. FLUENT_LOGGER.atInfo().log("Hello --- INFO --- (Flogger)...");

29. FLUENT_LOGGER.atSevere().log("Hello --- SEVERE --- (Flogger)...");

31. // ###############

33. final Level level = Level.INFO;

34. JDK_LOGGER.log(level, "Hello amigo (jdk) !!!");

35. FLUENT_LOGGER.at(level)//

36. .log("Hello amigo (Flogger) !!!");

38. // ###############

40. final String name = "John";

41. FLUENT_LOGGER.at(level)//

42. .log("My name is " + name + " and I love you (Flogger)");

43. FLUENT_LOGGER.at(level)//

44. .log("My name is %s and I still love you (Flogger)", name);

45. if (FLUENT_LOGGER.at(level).isEnabled()) {

46. FLUENT_LOGGER.at(level)//

47. .log("My name is " + name + " and I love you (Flogger)");

48. }

50. // ###############

52. FLUENT_LOGGER.atFine() //

53. .log("I am %s and I like logs...", doSomethingVeryLong(name));

54. FLUENT_LOGGER.atFine() //

55. .log("I am %s and I like lazy logs...", lazy(() -> doSomethingVeryLong(name)));

57. // ###############

59. try {

60. throw new FileNotFoundException("The file foo.txt was not found.");

61. } catch (final FileNotFoundException e) {

62. FLUENT_LOGGER.atInfo()//

63. .withCause(e) //

64. .log("I am %s and I am not happy!", name);

65. }

67. }

69. private static String doSomethingVeryLong(final String name) {

70. ...

71. }

73. }

log4j.properties

1. log4j.rootLogger=DEBUG, stdout

3. log4j.appender.stdout=org.apache.log4j.ConsoleAppender

4. log4j.appender.stdout.Target=System.out

5. log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

6. log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p [%c{1}:%L] - %m%n

my-logging.properties

1. handlers = java.util.logging.ConsoleHandler

2. .level = FINE

3. java.util.logging.ConsoleHandler.level = FINE

4. java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

5. java.util.logging.SimpleFormatter.format = %1$tF %1$tT %4$-7s [%2$s] - %5$s %n