Un guide pour les développeurs Java
L'efficacité du processus de développement logiciel repose en grande partie sur une journalisation adéquate. Face à la diversité des frameworks de journalisation Java disponibles, il est primordial de sélectionner celui qui allie facilité d'utilisation, fonctionnalités avancées, performance et adaptabilité. Log4j2, une bibliothèque de journalisation Java gratuite, répond précisément à ces exigences.
L'intégration de Log4j2 dans une application ouvre la voie à des options telles que le filtrage précis, la prise en charge de Java 8 lambda, la recherche de propriétés et la définition de niveaux de journalisation personnalisés. Découvrons comment implémenter Log4j2 dans vos projets et comment ses fonctionnalités peuvent optimiser votre travail.
Qu'est-ce que Log4j2 ?
La journalisation consiste à collecter des informations utiles, les journaux, qui peuvent être consultées et analysées ultérieurement. Ces journaux facilitent le débogage rapide du code d'une application, en aidant à comprendre le déroulement du code et à identifier et corriger les problèmes et les erreurs en production.
Outre les aspects liés au diagnostic, les journaux sont également employés à des fins d'audit. Par exemple, ils permettent de vérifier si une notification a été envoyée avec succès à un utilisateur.
Log4j2 est l'une des bibliothèques de journalisation Java les plus répandues. Il s'agit du successeur de la bibliothèque Log4j, qui a marqué son époque. Développé par l'Apache Software Foundation dans le cadre d'Apache Logging Services, Log4j2 est un logiciel libre et open source (FOSS) distribué sous la licence Apache, version 2.0.
Log4j2 s'appuie sur les bases solides de Log4j. L'utilisation d'un enregistreur offre des avantages significatifs par rapport à de simples instructions System.out.println(). Il permet notamment de contrôler les messages affichés et d'éviter le débordement des journaux. Disposer de journaux pertinents est essentiel dans un environnement de production où les débogueurs ne sont pas accessibles.
Comment intégrer Log4j2 à votre projet ?
Plusieurs méthodes existent pour ajouter Log4j2 à un projet Java. Il est conseillé d'utiliser Java 8 ou une version supérieure afin de profiter pleinement des fonctionnalités offertes par Log4j2.
Explorons les différentes manières d'intégrer Log4j2 en fonction de vos besoins spécifiques.
Ajout de Log4j2 aux projets avec Apache Maven
Si votre projet utilise Apache Maven comme système de construction, les dépendances de Log4j2 doivent être déclarées dans le fichier pom.xml.
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
</dependency>
</dependencies>
Pour simplifier la gestion des versions des différents artefacts, Log4j2 propose un fichier pom.xml de nomenclature (BOM). En l'intégrant dans votre gestion des dépendances, vous n'aurez plus à spécifier les versions individuellement.
<!-- Ajouter le BOM à la gestion des dépendances -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-bom</artifactId>
<version>2.20.0</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<!-- Une fois le BOM ajouté, les versions ne sont plus nécessaires -->
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</dependency>
</dependencies>
Ajout de Log4j2 aux projets avec Apache Gradle
Si vous utilisez Apache Gradle comme outil de construction, vous pouvez ajouter les dépendances Log4j2 dans votre fichier build.gradle.
dependencies {
implementation 'org.apache.logging.log4j:log4j-api:2.20.0'
implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
}
Si vous utilisez Gradle version 5.0 ou ultérieure, vous pouvez importer la nomenclature Maven de Log4j2 pour garantir la cohérence des versions des dépendances. Pour ce faire, ajoutez la configuration suivante à votre fichier build.gradle.
dependencies {
implementation platform('org.apache.logging.log4j:log4j-bom:2.20.0')
implementation 'org.apache.logging.log4j:log4j-api'
runtimeOnly 'org.apache.logging.log4j:log4j-core'
}
Pour les versions de Gradle 2.8 à 4.10, l'importation directe de la nomenclature Maven n'est pas possible. Vous devrez ajouter un plugin supplémentaire pour la gestion des dépendances.
plugins {
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
dependencyManagement {
imports {
mavenBom 'org.apache.logging.log4j:log4j-bom:2.20.0'
}
}
dependencies {
implementation 'org.apache.logging.log4j:log4j-api'
runtimeOnly 'org.apache.logging.log4j:log4j-core'
}
Ajout de Log4j2 aux applications autonomes sans outil de construction
Si votre projet ne nécessite pas d'outil de génération, vous pouvez télécharger les artefacts nécessaires de Log4j2 à partir de la page de téléchargement officielle.
Après le téléchargement, assurez-vous que le classpath de votre application comprend les fichiers JAR suivants :
log4j-api-2.20.0.jarlog4j-core-2.20.0.jar
Quels sont les composants de Log4j2 ?
Pour maîtriser les fonctionnalités de Log4j2, il est essentiel de comprendre sa structure. Log4j2 est composé de plusieurs éléments fondamentaux que nous allons détailler.
#1. LoggerContext
Le LoggerContext est le cœur du système de journalisation. Il contient tous les Loggers demandés par l'application, ainsi qu'une référence à la configuration.
#2. Configuration
La configuration inclut toutes les informations nécessaires au système de journalisation, telles que les enregistreurs, les appenders et les filtres. Dans Log4j2, la configuration peut être définie à l'aide de divers formats de fichiers tels que XML, JSON et YAML, ou par programmation via l'API de Log4j2.
Un rechargement automatique de la configuration est déclenché lors de la modification d'une propriété, évitant ainsi un redémarrage de l'application.
#3. Enregistreur
Le Logger est le principal composant de Log4j2. Les enregistreurs sont obtenus dans le code de l'application via l'instruction LogManager.getLogger() et permettent de générer les journaux. Les messages de journal peuvent être créés avec différents niveaux de gravité : débogage, info, avertissement, erreur et fatal.
#4. LoggerConfig
Le LoggerConfig est responsable du comportement d'un Logger spécifique. Il définit les paramètres de journalisation des événements générés par cet enregistreur, notamment le niveau de journalisation, les appenders utilisés et les filtres appliqués.
#5. Filtre
Dans Log4j2, les filtres permettent de traiter de manière sélective les événements de journalisation en fonction de critères spécifiques. Ces filtres peuvent être appliqués aux enregistreurs ou aux appenders. Ils contrôlent les événements de journal autorisés à passer dans le pipeline de journalisation, ce qui permet d'affiner le comportement de journalisation et de ne traiter que les journaux pertinents.
#6. Appender
L'Appender définit la destination d'un message de journal. Un Logger peut avoir plusieurs Appenders, et un événement de journal sera envoyé à chacun d'entre eux. Log4j2 propose de nombreux Appenders préconfigurés. Par exemple, le ConsoleAppender envoie les messages vers la console, et le FileAppender les enregistre dans un fichier. Chaque Appender utilise sa propre mise en page, qui détermine l'apparence du message de journal final.
#7. Mise en page
Dans Log4j2, la mise en page, ou Layout, définit l'aspect final du message de journal. Un Layout est associé à un Appender. L'Appender choisit la destination de la sortie, tandis que le Layout définit la manière dont le message sera formaté.
Top 5 des fonctionnalités de Log4j2
Log4j2 se distingue par ses nombreuses fonctionnalités, qui en font un framework de journalisation Java unique. Des enregistreurs asynchrones à la prise en charge des expressions lambda Java 8, Log4j2 offre des avantages considérables par rapport à ses concurrents. Examinons certaines de ses caractéristiques notables.
#1. Extension des fonctionnalités grâce aux plugins
Dans Log4j 1.x, la création d'extensions nécessitait de nombreuses modifications du code. Log4j2 résout ce problème d'extensibilité en introduisant un système de plugins.
Il est possible de déclarer un nouveau plugin en utilisant l'annotation @Plugin sur une classe. Grâce à la puissance des plugins, vous pouvez créer vos propres composants tels que des filtres ou des appenders. Il est également facile d'ajouter des composants tiers à la bibliothèque.
#2. Prise en charge de Java 8 Lambda
La prise en charge des expressions lambda Java 8 a été introduite à partir de la version 2.4 de Log4j2. Avec les expressions lambda, vous pouvez définir votre logique de journalisation de manière concise. Cela réduit la nécessité de contrôles sur plusieurs lignes ou de classes internes anonymes. Cela garantit également que les méthodes coûteuses ne sont exécutées qu'en cas de besoin, ce qui réduit la surcharge du système.
Prenons l'exemple d'un journal qui enregistre le résultat d'une opération coûteuse, mais uniquement si le niveau de débogage est activé. Avant la prise en charge des lambdas, cela nécessitait le code suivant :
if (logger.isDebugEnabled()) {
logger.debug("Le résultat de l'opération est : {}", expensiveOperation());
}
La répétition de ce type de condition introduisait des contrôles conditionnels superflus. Avec Log4j2, la même action peut être exprimée ainsi :
logger.debug("Le résultat de l'opération est : {}", () -> expensiveOperation())
La méthode expensiveOperation() n'est évaluée que si le niveau de débogage est activé, sans nécessiter de contrôle explicite.
#3. Enregistreurs asynchrones
Chaque événement de journalisation est une opération d'E/S qui peut augmenter la charge du système. Pour atténuer ce problème, Log4j2 introduit des enregistreurs asynchrones qui s'exécutent dans un thread séparé du thread principal de l'application. Ainsi, après avoir appelé la méthode logger.log(), le thread appelant reprend immédiatement le contrôle.
Cela lui permet de poursuivre l'exécution du code de l'application sans attendre la fin de l'événement de journalisation. L'exploitation de ce comportement asynchrone améliore le débit de journalisation. Il est possible de rendre tous les enregistreurs asynchrones par défaut ou de combiner comportements synchrone et asynchrone.
#4. Journalisation sans déchet
En Java, le ramasse-miettes est un processus qui élimine automatiquement les objets inutilisés. Bien que cette opération soit automatique, elle entraîne une surcharge.
Si une application crée trop d'objets en peu de temps, le processus de ramasse-miettes peut monopoliser une part importante des ressources système. De nombreuses bibliothèques de journalisation, y compris les versions précédentes de Log4j, créaient de nombreux objets temporaires pendant la journalisation. La pression accrue sur le ramasse-miettes avait alors un impact négatif sur les performances.
Depuis la version 2.6, Log4j2 fonctionne en mode "garbage-free" par défaut. Il réutilise les objets existants et réduit considérablement la création d'objets temporaires.
Les images ci-dessous illustrent l'amélioration apportée par Log4j2 version 2.6, qui réduit la création d'objets inutiles par rapport à Log4j2 version 2.5.
Dans Log4j2 version 2.5, de nombreux objets temporaires sont créés pendant le processus de journalisation ; Source : apache.org
Dans Log4j2.6, aucun objet temporaire n'est créé pendant le processus de journalisation ; Source : apache.org
#5. Recherches
Log4j2 permet d'enrichir les journaux avec des informations contextuelles en utilisant les Lookups. Ces recherches permettent d'intégrer des données provenant de diverses sources telles que les propriétés système, les variables d'environnement ou des valeurs personnalisées. Ainsi, vous pouvez inclure des informations pertinentes extraites dynamiquement, ce qui rend les journaux plus informatifs.
Prenons l'exemple d'un journal qui enregistre l'identifiant de session utilisateur pour chaque ligne de journal. Cela permettrait de retrouver tous les journaux correspondant à un identifiant de session spécifique.
Une approche naïve consisterait à ajouter explicitement l'identifiant de session dans chaque ligne de journal, ce qui serait difficile à maintenir et exposerait au risque d'omettre cette information.
logger.info("Les données de l'utilisateur ont été extraites pour la session id {}", sessionId);
...
logger.info("La transaction a été traitée pour la session id {}", sessionId);
...
logger.info("La requête a été traitée avec succès pour la session id {}", sessionId);
Une meilleure approche consiste à utiliser la recherche de carte contextuelle. L'identifiant de session peut être ajouté au contexte du thread dans le code de l'application, et sa valeur peut ensuite être utilisée dans la configuration Log4j2. Cette approche élimine le besoin de mentionner explicitement l'identifiant de session dans les messages du journal.
ThreadContext.put("sessionId", sessionId);
Une fois la valeur ajoutée, elle est accessible via le mot-clé ctx dans les Lookups.
<File name="Application" fileName="application.log">
<PatternLayout>
<pattern>%d %p %c{1.} [%t] $${ctx:sessionId} %m%n</pattern>
</PatternLayout>
</File>
Comment créer des niveaux de journal personnalisés dans Log4j2 ?
Les niveaux de journalisation dans Log4j2 permettent de classer les événements de journalisation en fonction de leur gravité. Le niveau de journalisation d'un message est spécifié lors de sa création dans le code, par exemple logger.debug() pour le niveau DEBUG ou logger.error() pour le niveau ERROR. Cela détermine quels messages seront finalement affichés. Le niveau de journalisation global est configuré dans le fichier de configuration.
Les niveaux de journalisation prédéfinis dans Log4j2, ainsi que leurs valeurs numériques associées, sont les suivants :
OFF(0), FATAL(100), ERROR(200), WARN(300), INFO(400), DEBUG(500), TRACE(600), ALL(MAX VALUE)
Si le niveau de journalisation est défini à une valeur particulière, tous les messages de ce niveau et des niveaux supérieurs (avec une valeur inférieure) seront affichés. Les autres messages seront ignorés.
Par exemple, si le niveau de journalisation est défini à WARN, les messages WARN, ERROR et FATAL seront affichés. Tout message d'un niveau différent sera ignoré. Cela est particulièrement utile pour exécuter le même code dans différents environnements.
Dans un environnement de développement, vous pouvez définir le niveau de journalisation à INFO ou DEBUG pour afficher plus de journaux et faciliter le débogage. En production, vous préférerez souvent définir le niveau à ERROR pour vous concentrer sur les problèmes réels sans être submergé par des journaux inutiles.
Il est parfois nécessaire de créer des niveaux de journalisation personnalisés. Log4j2 offre cette possibilité. Découvrons comment ajouter vos propres niveaux de journalisation et les utiliser dans votre application.
#1. Ajout d'un niveau de journal personnalisé via le fichier de configuration
Vous pouvez ajouter des niveaux de journalisation personnalisés en les déclarant dans le fichier de configuration.
Dans l'exemple ci-dessous, un niveau de journal personnalisé nommé NOTICE est défini avec la valeur 450. Il est placé entre INFO (valeur 400) et DEBUG (valeur 500). Ainsi, si le niveau est défini à NOTICE, les messages INFO seront journalisés, mais les messages DEBUG seront ignorés.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<CustomLevels>
<CustomLevel name="NOTICE" intLevel="450" />
</CustomLevels>
<Appenders>
<File name="MyFile" fileName="logs/app.log">
<PatternLayout pattern="%d %-7level %logger{36} - %msg%n"/>
</File>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="MyFile" level="NOTICE" />
</Root>
</Loggers>
</Configuration>
#2. Ajout d'un niveau de journal personnalisé dans le code
Outre la déclaration dans le fichier de configuration, vous pouvez également définir vos propres niveaux de journalisation personnalisés dans votre code.
final Level VERBOSE = Level.forName("VERBOSE", 550);
Cela crée un nouveau niveau de journal nommé VERBOSE. Ce niveau de journalisation se situera entre DEBUG (valeur 500) et TRACE (valeur 600). Si l'enregistreur est défini au niveau VERBOSE, tous les messages de niveau VERBOSE et supérieur seront journalisés, y compris DEBUG. Cependant, les messages TRACE seront ignorés.
#3. Utilisation d'un niveau de journal personnalisé dans le code
Les niveaux de journalisation personnalisés doivent être déclarés avant d'être utilisés, soit dans le fichier de configuration, soit dans le code. Une fois déclarés, vous pouvez les utiliser librement.
L'exemple de code suivant montre comment déclarer un niveau personnalisé nommé NOTICE et l'utiliser.
final Level NOTICE = Level.forName("NOTICE", 550);
final Logger logger = LogManager.getLogger();
logger.log(NOTICE, "a notice level message");
Bien que cela génère le message requis avec le nouveau niveau, il peut devenir fastidieux de toujours spécifier explicitement le niveau. Heureusement, il est possible de générer du code source pour créer des méthodes d'assistance pour la journalisation avec vos niveaux personnalisés. Ainsi, vous pourrez utiliser votre propre méthode logger.notice() de la même manière que vous utiliseriez logger.debug() ou logger.error().
Log4j2 est livré avec un utilitaire qui permet de créer des enregistreurs étendus. La commande suivante crée un fichier Java appelé CustomLogger.java, qui contient les méthodes de journalisation existantes, ainsi que les nouvelles méthodes pour le niveau NOTICE.
java -cp log4j-core-2.20.0.jar org.apache.logging.log4j.core.tools.ExtendedLoggerGenerator
com.example.CustomLogger NOTICE=450 > com/example/CustomLogger.java
Une fois le fichier généré, vous pouvez utiliser la classe dans votre code pour créer de nouveaux enregistreurs. Ces enregistreurs contiennent des méthodes supplémentaires pour votre niveau de journalisation personnalisé, étendant ainsi les capacités de vos enregistreurs.
final Logger logger = CustomLogger.create(ValueFirstSmsSender.class);
//cette nouvelle méthode est similaire à l'utilisation de logger.debug()
logger.notice("a notice level message");
Conclusion
Log4j2 est un framework de journalisation Java puissant qui offre un large éventail de fonctionnalités, de configurations et d'améliorations de performances. Les journaux étant une partie essentielle du processus de développement logiciel, disposer d'un framework robuste comme Log4j2 améliore les capacités d'une application.
La flexibilité et l'extensibilité de Log4j2 permettent une capture précise des événements qui se produisent dans l'application. Les journaux deviennent ainsi un outil puissant pour le débogage et l'audit. Grâce à ses fonctionnalités avancées et ses améliorations, Log4j2 se démarque comme le choix privilégié dans un large éventail de projets logiciels.
Vous pourriez également être intéressé par ces IDE Java et ces compilateurs en ligne.