Présentation

modifier

Log4j est un outil de journalisation permettant une personnalisation des logs pour chaque programme. Il peut être utile pour déboguer ou garder les traces de fonctionnement d'un programme déjà en production. On utilisera Maven pour gérer les dépendances.


Hello world!

modifier

pom.xml

modifier

Pour utiliser les fonctionnalités de Maven, on peut modifier le fichier pom.xml afin d'intégrer les bibliothèques de log4j :

<project xmlns="http://maven.apache.org/POM/4.0.0" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>SimpleJavaProject</groupId>
 <artifactId>SimpleJavaProject</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <properties>
  <org.log4j-version>2.9</org.log4j-version>
 </properties>
 <build>
  <sourceDirectory>src</sourceDirectory>
  <plugins>
   <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.3</version>
    <configuration>
     < source/>
     <target/>
    </configuration>
   </plugin>
  </plugins>
 </build>
 <dependencies>
  <dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-api</artifactId>
   <version>${org.log4j-version}</version>
  </dependency>
  <dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-core</artifactId>
   <version>${org.log4j-version}</version>
  </dependency>
 </dependencies>
</project>

Ici on ajoute un bloc <dependencies> avec une seule dépendance concernant log4j. La version est indiquée via une propriété, ici la 2.8.2, définie plus haut dans le fichier pom.xml. C'est une bonne pratique d'éviter d'avoir des numéros de version en dur directement dans les dépendances afin de rendre le fichier plus générique et ainsi plus facilement maintenable.

Pour inclure la bibliothèque dans le projet, il est nécessaire de mettre à jour le projet via clique droit sur le projet, puis Maven, puis Update Project, on obtient alors cette fenêtre.

 

Fichier de configuration de log4j

modifier

Une fois que la librairie a été ajoutée, il faut créer un fichier de configuration afin de fournir les informations à log4j pour fonctionner. Ce fichier peut être au format xml ou au format texte, finissant par .properties. Le format xml est moins facile à lire mais offre plus de possibilités.

Il faut créer un répertoire « resources » dans le projet et placer un fichier log4j2.xml dedans :

 

Le fichier ci-dessous permet une écriture dans la console :

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
 <Appenders>
  <Console name="STDOUT" target="SYSTEM_OUT">
   <PatternLayout pattern="%d - %-5p - %C{10}:%L - %m%n"/>
  </Console>
 </Appenders>
 <Loggers>
  <Logger name="org.apache.log4j.xml" level="info"/>
   <Root level="debug">
    <AppenderRef ref="STDOUT"/>
   </Root>
 </Loggers>
</Configuration>

La propriété version est indispensable et indique le numéro de version de xml. L'encodage, via le paramètre encoding, est facultatif mais recommandé, utf-8 est à préconiser pour son universalité dans les années 2010. Dans la balise PatternLayout on indique le modèle de conversion, pattern. Il va permettre de spécifier le format des données dans chaque ligne. Le %d va permettre d'indiquer les dates et heures. %-5p indique le niveau de log avec toujours 5 caractères d'utilisés ce qui permet de mieux aligner les lignes. %C indique la classe et %L le numéro de ligne du log ceci est important en cas de débogage. Le nombre entre accolades est le niveau de précision, s'il n'y a pas de problème de performance, il est opportun de le mettre à un niveau élevé, 10 par exemple. %m est pour le message tandis que %n est un retour à la ligne. Plus d'informations ici.

Il est important d'indiquer la date et l'heure pour savoir quand ont eu lieu les évènements, notamment en production. Il faut garder en tête que les logs sont souvent la première source d'information quand un incident arrive en production, il faut donc qu'ils soient lisibles et explicites, y compris en situation d'urgence.

Quand la librairie et les fichiers de configuration ont été ajoutés, il faut modifier la classe principale pour faire l'affichage. On utilise le niveau de log information, via .info ici :

package mainPackage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Logger;
public class MainClass 
{
 private static final Logger logger = (Logger) LogManager.getLogger();
 public static void main(String[] args) 
 {
  logger.info("Hello world!");
 }
}

Résultat

modifier

Cela donne le résultat suivant :

 

On peut voir que l'on a les millisecondes ici, ceci peut être utile pour avoir une idée plus précise des méthodes consommant du temps processeur.

Fichiers de configuration multiples

modifier

Par défaut la configuration de log4j va se faire via ce répertoire appelé resources avec un fichier log4j2.xml.

On peut modifier en indiquant un autre fichier de configuration :

 

Dans ce fichier on peut modifier par rapport au premier en enlevant les millisecondes, on indique explicitement le format de date et heure que l'on souhaite :

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
 <Appenders>
  <Console name="STDOUT" target="SYSTEM_OUT">
   <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} - %-5p - %C{2}:%L - %m%n"/>
  </Console>
 </Appenders>
 <Loggers>
  <Logger name="org.apache.log4j.xml" level="info"/>
   <Root level="debug">
    <AppenderRef ref="STDOUT"/>
   </Root>
 </Loggers>
</Configuration>

Pour donner explicitement le chemin du nouveau fichier on modifie le main de la manière suivante :

package mainPackage;
import java.io.File;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
public class MainClass 
{
 private static final Logger logger = (Logger) LogManager.getLogger();
 public static void main(String[] args) 
 {
  LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);
  File file = new File("newResourcesFolder/newLog4j2file.xml");
  context.setConfigLocation(file.toURI());
  logger.info("Hello world!");
 }
}

Le résultat est le suivant :

 

Écriture dans un fichier

modifier

Dans l'exemple précédent on écrivait directement dans la console. C'est pratique pour déboguer mais inutile en production, ceci peut être néfaste si cela écrit directement dans catalina.out. Il est donc nécessaire de configurer le fichier log4j.xml pour avoir également une écriture dans un fichier :

 <?xml version="1.0" encoding="UTF-8"?>
 <Configuration>
  <Appenders>
   <Console name="STDOUT" target="SYSTEM_OUT">
    <PatternLayout pattern="%d - %-5p - %C{2}:%L - %m%n"/>
   </Console>
   <File name="fileOut" fileName="C:\\Spring\\SimpleJavaProject.log">
    <PatternLayout pattern="%d - %-5p - %C{2}:%L - %m%n"/>
   </File>        
  </Appenders>
  <Loggers>
   <Logger name="org.apache.log4j.xml" level="info"/>
   <Root level="debug">
    <AppenderRef ref="STDOUT"/>
    <AppenderRef ref="fileOut"/>
   </Root>
  </Loggers>
 </Configuration>

Ici on ajout donc un appender nommé fileOut qui écrit dans le fichier C:\\Spring\\SimpleJavaProject.log. Comme on laisse l'appender STDOUT on obtient les deux sorties pour une même ligne de log avec le fichier créé :

 

Le main générant le log peut être réduit au minimum :

 package mainPackage;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 public class MainClass 
 {
  private static final Logger logger = (Logger) LogManager.getLogger();
  public static void main(String[] args) 
  {
   logger.info("test");
  }
 }

Dans le contenu du fichier on obtient le même résultat qu'avec la console :

2017-07-24 14:29:12,892 - INFO  - mainPackage.MainClass:13 - test

Rotation des logs

modifier

La rotation de log est la gestion des fichiers de logs lorsque ceux-ci ont atteint une taille ou une ancienneté maximum.

Rotation selon la taille

modifier

On modifie le fichier de log de la manière suivante pour obtenir une rotation avec une taille de 100 Ko maximum par fichier :

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
 <Appenders>
  <Console name="STDOUT" target="SYSTEM_OUT">
   <PatternLayout pattern="%d - %-5p - %C{2}:%L - %m%n"/>
  </Console>
  <RollingFile name="fileOut" fileName="C:\\Spring\\SimpleJavaProject.log" filePattern="C:\\Spring\\archive - %d{yyyy-MM-dd-HH-mm-ss-sss}.log">
    <Policies>
     <SizeBasedTriggeringPolicy size="100KB"/>
   </Policies>
  </RollingFile>        
 </Appenders>
 <Loggers>
  <Logger name="org.apache.log4j.xml" level="info"/>
  <Root level="debug">
   <AppenderRef ref="STDOUT"/>
   <AppenderRef ref="fileOut"/>
  </Root>
 </Loggers>
</Configuration>

Ensuite on modifie le main pour un affichage de 100 000 fois "Hello World!" via log4j.

package mainPackage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class MainClass 
{
	  private static final Logger logger = (Logger) LogManager.getLogger();
	  public static void main (String[] args)
	  {
		  for(int counter=0;counter<100000;counter++)
			  logger.info("Hello World! "+counter);
	  }
}

Cela donne les fichiers générés suivant :

 

On voit qu'à chaque milliseconde un nouveau fichier d'environ 100 Ko est généré.

Rotation selon l'ancienneté

modifier

Sans changer le fichier main, on modifier ici le fichier log4j2.xml :

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
 <Appenders>
  <Console name="STDOUT" target="SYSTEM_OUT">
   <PatternLayout pattern="%d - %-5p - %C{2}:%L - %m%n"/>
  </Console>
  <RollingFile name="fileOut" fileName="C:\\Spring\\SimpleJavaProject.log" filePattern="C:\\Spring\\archive - %d{yyyy-dd-MM-HH-mm-ss-sss}.log">
      <PatternLayout 
          pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}" />
       <Policies>
        <TimeBasedTriggeringPolicy interval="1" modulate="true" />
      </Policies>
  </RollingFile>
 </Appenders>
 <Loggers>
  <Logger name="org.apache.log4j.xml" level="info"/>
  <Root level="debug">
   <AppenderRef ref="STDOUT"/>
   <AppenderRef ref="fileOut"/>
  </Root>
 </Loggers>
</Configuration>

Cela donne le résultat suivant :

 

On voit que chaque seconde un nouveau fichier est généré indépendamment de la taille. On constate également qu'il y a un changement pour les millisecondes, cela est lié au temps de traitement, log4j n'est pas déterministe.