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.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
<properties>
<project.build.sourceEncoding>
UTF-8</project.build.sourceEncoding>
<maven.compiler.source>
1.8</maven.compiler.source>
<maven.compiler.target>
1.8</maven.compiler.target>
<log4j.version>
1.2.17</log4j.version>
<flogger.version>
0.4</flogger.version>
<junit.version>
4.12</junit.version>
</properties>
<dependencies>
<!-- Log4j -->
<dependency>
<groupId>
log4j</groupId>
<artifactId>
log4j</artifactId>
<version>
${log4j.version}</version>
</dependency>
<!-- Flogger -->
<dependency>
<groupId>
com.google.flogger</groupId>
<artifactId>
flogger</artifactId>
<version>
${flogger.version}</version>
</dependency>
<dependency>
<groupId>
com.google.flogger</groupId>
<artifactId>
flogger-system-backend</artifactId>
<version>
${flogger.version}</version>
</dependency>
...
</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.
2.
3.
4.
5.
6.
log4j.rootLogger=
DEBUG, stdout
log4j.appender.stdout=
org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=
System.out
log4j.appender.stdout.layout=
org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%
d{
yyyy-
MM-
dd HH:mm:ss}
%-
5
p [%
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.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
public
class
Main {
private
static
final
org.apache.log4j.Logger LOG4J_LOGGER =
org.apache.log4j.Logger.getLogger
(
Main.class
.getName
(
));
public
static
void
main
(
String[] args) {
System.out.println
(
"Hello +++ ALL +++ (sysout)..."
);
// Log4j
LOG4J_LOGGER.debug
(
"Hello *** DEBUG *** (log4j)..."
);
LOG4J_LOGGER.info
(
"Hello *** INFO *** (log4j)..."
);
LOG4J_LOGGER.error
(
"Hello *** ERROR *** (log4j)..."
);
...
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.
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.
2.
3.
4.
5.
handlers =
java.util.logging.ConsoleHandler
.level =
FINE
java.util.logging.ConsoleHandler.level =
FINE
java.util.logging.ConsoleHandler.formatter =
java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format =
%
1
$tF %
1
$tT %
4
$-
7
s [%
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 .
-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.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
public
class
Main {
private
static
final
org.apache.log4j.Logger LOG4J_LOGGER =
org.apache.log4j.Logger.getLogger
(
Main.class
.getName
(
));
private
static
final
java.util.logging.Logger JDK_LOGGER =
java.util.logging.Logger.getLogger
(
Main.class
.getName
(
));
public
static
void
main
(
String[] args) {
System.out.println
(
"Hello +++ ALL +++ (sysout)..."
);
// Log4j
LOG4J_LOGGER.debug
(
"Hello *** DEBUG *** (log4j)..."
);
LOG4J_LOGGER.info
(
"Hello *** INFO *** (log4j)..."
);
LOG4J_LOGGER.error
(
"Hello *** ERROR *** (log4j)..."
);
// JDK
JDK_LOGGER.fine
(
"Hello ### FINE ### (jdk)..."
);
JDK_LOGGER.info
(
"Hello ### INFO ### (jdk)..."
);
JDK_LOGGER.severe
(
"Hello ### SEVERE ### (jdk)..."
);
...
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.
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.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
import
com.google.common.flogger.FluentLogger;
public
class
Main {
private
static
final
org.apache.log4j.Logger LOG4J_LOGGER =
org.apache.log4j.Logger.getLogger
(
Main.class
.getName
(
));
private
static
final
java.util.logging.Logger JDK_LOGGER =
java.util.logging.Logger.getLogger
(
Main.class
.getName
(
));
private
static
final
FluentLogger FLUENT_LOGGER =
FluentLogger.forEnclosingClass
(
);
public
static
void
main
(
String[] args) {
System.out.println
(
"Hello +++ ALL +++ (sysout)..."
);
// Log4j
LOG4J_LOGGER.debug
(
"Hello *** DEBUG *** (log4j)..."
);
LOG4J_LOGGER.info
(
"Hello *** INFO *** (log4j)..."
);
LOG4J_LOGGER.error
(
"Hello *** ERROR *** (log4j)..."
);
// JDK
JDK_LOGGER.fine
(
"Hello ### FINE ### (jdk)..."
);
JDK_LOGGER.info
(
"Hello ### INFO ### (jdk)..."
);
JDK_LOGGER.severe
(
"Hello ### SEVERE ### (jdk)..."
);
// Flogger
FLUENT_LOGGER.atFine
(
).log
(
"Hello --- FINE --- (Flogger)..."
);
FLUENT_LOGGER.atInfo
(
).log
(
"Hello --- INFO --- (Flogger)..."
);
FLUENT_LOGGER.atSevere
(
).log
(
"Hello --- SEVERE --- (Flogger)..."
);
...
Sans surprise, à l’exécution du programme, on ne voit pas de différence entre Flogger et Java Logging.
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.
2.
3.
4.
final
Level level =
Level.INFO;
JDK_LOGGER.log
(
level, "Hello amigo (jdk) !!!"
);
FLUENT_LOGGER.at
(
level)
.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.
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.
2.
3.
4.
5.
6.
final
Level level =
Level.INFO;
final
String name =
"John"
;
FLUENT_LOGGER.at
(
level)
.log
(
"My name is "
+
name +
" and I love you (Flogger)"
);
FLUENT_LOGGER.at
(
level)
.log
(
"My name is %s and I still love you (Flogger)"
, name);
À l’exécution, on ne constate toutefois aucune différence.
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 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
final
Level level =
Level.INFO;
final
String name =
"John"
;
// Concat
if
(
FLUENT_LOGGER.at
(
level).isEnabled
(
)) {
FLUENT_LOGGER.at
(
level)
.log
(
"My name is "
+
name +
" and I love you (Flogger)"
);
}
// printf
FLUENT_LOGGER.at
(
level)
.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.
2.
3.
4.
FLUENT_LOGGER.atFine
(
)
.log
(
"My complete name is %s and I am not your father :-p"
, person.toString
(
));
FLUENT_LOGGER.atFine
(
)
.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.
2.
FLUENT_LOGGER.atFine
(
)
.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é.
2.
3.
final
String name =
"John"
;
FLUENT_LOGGER.atFine
(
)
.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.
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 :
2.
3.
4.
5.
6.
7.
8.
try
{
...
throw
new
FileNotFoundException
(
"The file foo.txt was not found."
);
}
catch
(
final
FileNotFoundException e) {
FLUENT_LOGGER.atInfo
(
)
.withCause
(
e)
.log
(
"I am %s and I am not happy!"
, name);
}
L’exécution de ce bloc de code va masquer l’exception, ce qui n’est pas forcément ce qu’on souhaite.
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.
2.
3.
4.
5.
handlers =
java.util.logging.ConsoleHandler
.level =
FINE
java.util.logging.ConsoleHandler.level =
FINE
java.util.logging.ConsoleHandler.formatter =
java.util.logging.SimpleFormatter
#java.util.logging.SimpleFormatter.format =
%
1
$tF %
1
$tT %
4
$-
7
s [%
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.
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▲
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
package
com.ice.article.flogger;
import
static
com.google.common.flogger.LazyArgs.lazy;
import
java.io.FileNotFoundException;
import
java.util.logging.Level;
import
com.google.common.flogger.FluentLogger;
public
class
Main {
private
static
final
org.apache.log4j.Logger LOG4J_LOGGER =
org.apache.log4j.Logger.getLogger
(
Main.class
.getName
(
));
private
static
final
java.util.logging.Logger JDK_LOGGER =
java.util.logging.Logger.getLogger
(
Main.class
.getName
(
));
private
static
final
FluentLogger FLUENT_LOGGER =
FluentLogger.forEnclosingClass
(
);
public
static
void
main
(
String[] args) {
System.out.println
(
"Hello +++ ALL +++ (sysout)..."
);
LOG4J_LOGGER.debug
(
"Hello *** DEBUG *** (log4j)..."
);
LOG4J_LOGGER.info
(
"Hello *** INFO *** (log4j)..."
);
LOG4J_LOGGER.error
(
"Hello *** ERROR *** (log4j)..."
);
JDK_LOGGER.fine
(
"Hello ### FINE ### (jdk)..."
);
JDK_LOGGER.info
(
"Hello ### INFO ### (jdk)..."
);
JDK_LOGGER.severe
(
"Hello ### SEVERE ### (jdk)..."
);
FLUENT_LOGGER.atFine
(
).log
(
"Hello --- FINE --- (Flogger)..."
);
FLUENT_LOGGER.atInfo
(
).log
(
"Hello --- INFO --- (Flogger)..."
);
FLUENT_LOGGER.atSevere
(
).log
(
"Hello --- SEVERE --- (Flogger)..."
);
// ###############
final
Level level =
Level.INFO;
JDK_LOGGER.log
(
level, "Hello amigo (jdk) !!!"
);
FLUENT_LOGGER.at
(
level)//
.log
(
"Hello amigo (Flogger) !!!"
);
// ###############
final
String name =
"John"
;
FLUENT_LOGGER.at
(
level)//
.log
(
"My name is "
+
name +
" and I love you (Flogger)"
);
FLUENT_LOGGER.at
(
level)//
.log
(
"My name is %s and I still love you (Flogger)"
, name);
if
(
FLUENT_LOGGER.at
(
level).isEnabled
(
)) {
FLUENT_LOGGER.at
(
level)//
.log
(
"My name is "
+
name +
" and I love you (Flogger)"
);
}
// ###############
FLUENT_LOGGER.atFine
(
) //
.log
(
"I am %s and I like logs..."
, doSomethingVeryLong
(
name));
FLUENT_LOGGER.atFine
(
) //
.log
(
"I am %s and I like lazy logs..."
, lazy
((
) ->
doSomethingVeryLong
(
name)));
// ###############
try
{
throw
new
FileNotFoundException
(
"The file foo.txt was not found."
);
}
catch
(
final
FileNotFoundException e) {
FLUENT_LOGGER.atInfo
(
)//
.withCause
(
e) //
.log
(
"I am %s and I am not happy!"
, name);
}
}
private
static
String doSomethingVeryLong
(
final
String name) {
...
}
}
2.
3.
4.
5.
6.
log4j.rootLogger=
DEBUG, stdout
log4j.appender.stdout=
org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=
System.out
log4j.appender.stdout.layout=
org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%
d{
yyyy-
MM-
dd HH:mm:ss}
%-
5
p [%
c{
1
}
:%
L] -
%
m%
n
2.
3.
4.
5.
handlers =
java.util.logging.ConsoleHandler
.level =
FINE
java.util.logging.ConsoleHandler.level =
FINE
java.util.logging.ConsoleHandler.formatter =
java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format =
%
1
$tF %
1
$tT %
4
$-
7
s [%
2
$s] -
%
5
$s %
n