I. Références

  • La documentation ("légère" sur ce coup là) de Tomcat 6(http://tomcat.apache.org/tomcat-6.0-doc/logging.html)
  • La documentation et le source code de SLF4J(www.slf4j.org), LOG4J(logging.apache.org/log4j/1.2/index.html) et de Commons-logging(commons.apache.org/logging/)

II. Procédure pas-à-pas

II-i. Pré-requis

  • Télécharger la distribution binaire de Tomcat 6.0.18(testé uniquement avec cette version)
  • Télécharger les sources de Tomcat 6.0.18
  • Télécharger Ant
  • Télécharger la distribution de SLF4J
  • Télécharger la distribution de LogBack

II-ii. Modification du "fonctionnement" de Tomcat

Cette partie est directement reprise de la documentation de Tomcat(chapitre Logging).

II-ii-1. Installation de Tomcat

Rien de particulier, suivre les instructions d'Apache....

II-ii-2. Compilation des extras

  • Décompresser les sources de Tomcat
  • Exécuter la commande suivante : ant -f extras.xml
  • Cela génère des fichiers dans un répertoire output

II-ii-3. "Mise à jour" de Tomcat

  • Remplacer le fichier tomcat-juli.jar du répertoire bin de votre installation de tomcat par celui généré(TOMCATSRC/output/)
  • Copier le fichier tomcat-juli-adapters.jar dans le répertoire lib de Tomcat

II-iii. Mise en place des librairies de logging

Copier dans le répertoire lib de tomcat les jars suivants :

  • log4j-over-slf4j-VERSION.jar (distribution de SFL4J)
  • slf4j-api-VERSION.jar (distribution de SFL4J)
  • logback-core-VERSION.jar (distribution de LogBack)
  • logback-classic-VERSION.jar (distribution de LogBack)

Comme cela, cela devrait déjà marcher, mais la configuration basique de logback est en mode DEBUG, ce qui fait que Tomcat génère énormément de log, donc un simple fichier de configuration peut être utilisé : logback.xml (à mettre dans le répertoire lib de tomcat) :

 
Sélectionnez

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
	<contextName>Tomcat</contextName>
	<jmxConfigurator contextName="Tomcat" />
		<appender name="FILE" class="ch.qos.logback.core.FileAppender">
    	<File>${catalina.home}\logs\tomcat.log</File>
        <Append>true</Append>
        <BufferedIO>false</BufferedIO>
        <ImmediateFlush>true</ImmediateFlush>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d{yyyy-MM-dd HH:mm:ss} [%-5p] - %m%n</Pattern>
        </layout>
    </appender>
    <root>
        <level value="INFO" />
        <appender-ref ref="FILE" />
    </root>
</configuration>

III. Pourquoi ça marche ?

L'explication peut paraitre simpliste, mais finalement, le fait que ça marche est essentiellement du bon sens (je me demande d'ailleurs pourquoi j'y ai passé deux jours entiers....). En fait, le principe est le "truchement de classloader" ou polymorphisme(je crois en langage professionel...). Voici donc le cheminement d'un log Tomcat depuis la ligne de code originelle à son fichier(par exemple) :

III-i. tomcat-juli.jar

La nouvelle version du fichier qui est mis en place ne contient plus l'"hardcodage" du logger JULI, à la place il propose des interfaces sur org.apache.juli.logging, mais pas d'implémentation.

III-ii. tomcat-juli-adapters.jar

Ce jar contient une implémentation du LoggerFactory, qui en fait cherche dans le classpath une implémentation de logging JDK ou log4j. Evidemment, s'il cherchait du slf4j, cela simplifierai bien des choses.

III-iii. log4j-over-slf4j-VERSION.jar et slf4j-api-VERSION.jar

C'est justement pour cela que l'on met ces archives. En fait elle remplace une éventuelle librairie log4j.jar, et place l'environnement dans une position : "je choisis le logger que je trouve". Ce qui est exactement le rôle de slf4j(Standard Logging Facage for Java).

III-iv. logback-core-VERSION.jar et logback-classic-VERSION.jar

Enfin, notre choix se porte sur l'implémentation LogBack, ce qui nous conduit à l'utilisation de ces deux archives, qui contiennent le code pour écrire finalement nos logs.

IV. Conclusion

Enfin, notre choix se porte sur l'implémentation LogBack, ce qui nous conduit à l'utilisation de ces deux archives, qui contiennent le code pour écrire finalement nos logs.

V. Configuration d'un logger par application

V-i. Mise en place d'un ContextSelector

  • Rajout de -Dlogback.ContextSelector=JNDI dans le lancement de Tomcat.
  • Rajout dans le web.xml de chaque application :
 
Sélectionnez

<env-entry>
	<env-entry-name>logback/context-name</env-entry-name>
	<env-entry-type>java.lang.String</env-entry-type>
	<env-entry-value>TestLog</env-entry-value>
</env-entry>		
  • Le fichier de paramétrage devra s'appeler logback-TestLog.xml et être mis dans le répertoire WEB-INF/classes de l'application
  • Le contexte de chaque application doit être nettoyé lorsque l'application s'arrête. Pour cela, il faut rajouter la configuration suivante dans le web.xml :
 
Sélectionnez

<listener>
	<listener-class>ch.qos.logback.classic.selector.servlet.ContextDetachingSCL</listener-class>
</listener>
  • Lors de la récupération du contexte, la requête JNDI peut être longue. En rajoutant, le filtre suivant dans le web.xml, cette requête est évité, car le contexte est stocké dans un Thread local à chaque requête http :
 
Sélectionnez

<filter>
	<filter-name>LoggerContextFilter</filter-name>
	<filter-class>ch.qos.logback.classic.selector.servlet.LoggerContextFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>LoggerContextFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

V-ii. Gestion du log en cours de production

L'idée de base est de pouvoir changer en exploitation le niveau de trace des applications. La première version de ce que j'avais imaginé était d'avoir plusieurs fichiers de config, et de proposer une servlet permettant de switché de fichier de configuration.

Finalement, l'option retenue est d'utiliser les fonctions JMX du serveur Tomcat et de la librairie LogBack. Pour cela, il faut tout d'abord autoriser Tomcat à utiliser JMX. Il suffit de rajouter ces paramètres à la chaîne de lancement de Tomcat :

 
Sélectionnez

-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8888
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false

Ensuite dans une commande DOS, il suffit de lancer l'application jconsole, de se connecter en remote sur le port 8888, d'aller sur l'onglet MBeans et de sélectionner la librairie de logging logback. Dans les différents écrans, il est possible de modifier en "live" le niveau de debug des applications.

Pour finir, il faut, comme pour le JNDI, nettoyer la mémoire lorsque l'application est arrêtée. Pour cela, il suffit de créer une classe du style :

 
Sélectionnez

package home.devcom.servlet.context;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.LoggerContext;

public class ContextListener implements ServletContextListener 
{
  public void contextDestroyed(ServletContextEvent sce) 
  {
	System.out.println("ContextListener::contextDestroyed");
	// Code repris de la documentation de logback : 
	//  http://logback.qos.ch/manual/jmxConfig.html
    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
    lc.stop();
  }

  public void contextInitialized(ServletContextEvent sce) 
  {}
} 

Compiler et transformer en fichier jar et le mettre dans le WEB-INF/lib de vos application puis de rajouter les lignes suivantes au web.xml :

 
Sélectionnez

<listener>
	<listener-class>home.devcom.servlet.context.ContextListener</listener-class>
</listener>