Log4J dans Tomcat

Cette page a été rédigée il y a fort fort longtemps, et n'a pas tellement été mise à jour.

 

Vous savez, moi je ne crois pas qu'il y ait de bonne ou de mauvaise page. Moi, si je devais résumer mon wiki aujourd'hui avec vous, je dirais que c'est d'abord des rencontres. Des gens qui m'ont tendu la main, peut-être à un moment où je ne pouvais pas, où j'étais seul chez moi. Et c'est assez curieux de se dire que les hasards, les rencontres forgent une destinée... Parce que quand on a le goût de la chose, quand on a le goût de la chose bien faite, le beau geste, parfois on ne trouve pas l'interlocuteur en face je dirais, le miroir qui vous aide à avancer. Alors ça n'est pas mon cas, comme je disais là, puisque moi au contraire, j'ai pu ; et je dis merci au wiki, je lui dis merci, je chante le wiki, je danse le wiki... je ne suis qu'amour ! Et finalement, quand des gens me disent « Mais comment fais-tu pour avoir cette humanité ? », je leur réponds très simplement que c'est ce goût de l'amour, ce goût donc qui m'a poussé aujourd'hui à entreprendre une construction logicielle... mais demain qui sait ? Peut-être simplement à me mettre au service de la communauté, à faire le don, le don de soi.

Dans sa configuration par défaut, Tomcat utilise le système de traces du JDK, appelé Java Logging API, ou JUL. Pour pallier aux faiblesses de ce système, Tomcat intègre une sur-couche appelée JULI , qui permet par exemple d’isoler la configuration d’une application Web, ou d’avoir plusieurs fichiers de traces.

On peut s’étonner de ce choix car Tomcat est développé par la fondation Apache qui héberge un projet logging, dont l’outil Log4J est la référence en matière de traces ! Heureusement, le concepteurs de Tomcat nous ont laissé la possibilité de remplacer JUL / JULI par Log4J, et de retrouver toutes les possibilités en terme de appenders, de layout et de filtres.

Installer Log4J

La procédure pour installer Log4J dans Tomcat diffère un peu entre Tomcat 5.5 et Tomcat 6.

Tomcat 5.5

Dans un premier temps, il faut télécharger Log4J et Apache Commons Logging.

Ensuite, il faut placer les fichier log4j-1.2.x.jar et commons-logging.jar dans le répertoire commons/lib de Tomcat.

Enfin, le fichier de paramétrage, log4j.properties ou log4j.xml, doit être placé dans le répertoire commons/classes de Tomcat. Si vous préférez un autre emplacement, comme le répertoire conf, par exemple, il faut l’indiquer à Tomcat avec la propriété -Dlog4j.configuration :

 export JAVA_OPTS="-Dlog4j.configuration=$CATALINA_HOME/conf/log4j.xml"

Cette procédure devrait suffire à remplacer JUL par Log4J.

La procédure peut être affinée en modifiant le script catalina.sh (.bat) :

 # Set Log4J if it is present
 if [ -r "$CATALINA_BASE"/conf/log4j.xml ]; then
   JAVA_OPTS="$JAVA_OPTS "-Dlog4j.configuration="file:$CATALINA_BASE/conf/log4j.xml"
 else
   if [ -r "$CATALINA_BASE"/conf/log4j.properties ]; then
     JAVA_OPTS="$JAVA_OPTS "-Dlog4j.configuration="file:$CATALINA_BASE/conf/log4j.properties"
   fi
 fi

Tomcat 6.0

La procédure est légèrement plus complexe pour la version 6 de Tomcat.

Le début ne change pas beaucoup, avec le téléchargement de Log4J et le placement du jar dans le répertoire lib de Tomcat (la structure des répertoires de librairies a été simplifiée). Il n’y a pas besoin de Commons Logging.

Ensuite, la documentation nous dit de générer des fichiers avec Ant et le script extra.xml, à partir du code source de Tomcat. J’ai trouvé plus simple de télécharger les fichiers depuis les archives de Tomcat ! Pour les versions les plus récentes (6.0.13), les fichiers extra sont livrés dans le répertoire bin/extra du site.

J’ai donc téléchargé tomcat-juli.jar, que j’ai mis dans bin/ de Tomcat, en remplacement du fichier existant, et tomcat-juli-adapters.jar que j’ai mis dans lib.

Le fichier de paramètre se positionne dans le répertoire lib, ou à l’aide de la propriété -Dlog4j.configuration.

Paramétrer Log4J

Paramétrage simple

La documentation de Tomcat propose un fichier de configuration simplifié qui peut faire l’affaire, à condition de réduire le volume, et donc de remplacer le niveau DEBUG par INFO.

 log4j.rootLogger=INFO, TOMCAT
 log4j.appender.TOMCAT=org.apache.log4j.RollingFileAppender
 log4j.appender.TOMCAT.File=${catalina.home}/logs/tomcat.log
 log4j.appender.TOMCAT.MaxFileSize=10MB
 log4j.appender.TOMCAT.MaxBackupIndex=10
 log4j.appender.TOMCAT.layout=org.apache.log4j.PatternLayout
 log4j.appender.TOMCAT.layout.ConversionPattern=%p %t %c - %m%n

Paramétrage à l’identique

Pour ceux qui se sont attachés aux traces générées par JUL, avec plusieurs fichiers, il est possible de reproduire le même fonctionnement avec Log4J. Il est assez simple d’obtenir un résultat approchant avec des DailyRollingFileAppender et un PatternLayout paramétrés comme ceci :

log4j.appender.CATALINA=org.apache.log4j.DailyRollingFileAppender
log4j.appender.CATALINA.File=${catalina.base}/logs/catalina.log
log4j.appender.CATALINA.DatePattern=.yyyy-MM-dd
log4j.appender.CATALINA.Threshold=DEBUG
log4j.appender.CATALINA.layout=org.apache.log4j.PatternLayout
log4j.appender.CATALINA.layout.ConversionPattern=%d{DATE} %C %M%n%5p %m%n

Des petites différences subsistent, dans le nom des fichiers et dans le format des dates. Ce dernier peut encore être affiné, au risque de réduire les performances :

log4j.appender.CATALINA.layout.ConversionPattern=%d{d MMM yyyy hh:mm:ss} %C %M%n%5p %m%n

Pour avoir exactement les mêmes noms de fichiers, il faut utiliser le RollingFileAppender des composants extra. Après téléchargement, on place le fichier apache-log4j-extras-1.0.jar dans le répertoire des librairies de Tomcat (lib/ ou common/lib/), puis on reprend la configuration au début, en XML, car ce appender est incompatible avec la configuration en fichier properties. Il faut aussi utiliser Log4J 1.2.15 (ou +), qui assure une bonne prise en compte des balises XML des composants extra.

En utilisant des appenders definis comme ci-dessous, on obtient un résultat très proche de ce que JUL produit. La seule différence est un espace en début de ligne !

 <appender name="CATALINA" class="org.apache.log4j.rolling.RollingFileAppender">
   <param name="threshold" value="DEBUG"/>
   <rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
     <param name="FileNamePattern" value="${catalina.base}/logs/catalina.%d{yyyy-MM-dd}.log"/>
   </rollingPolicy>

   <layout class="org.apache.log4j.PatternLayout">
     <param name="ConversionPattern" value="%d{d MMM yyyy hh:mm:ss} %C %M%n%5p %m%n"/>
   </layout>
 </appender>

Paramétrage complet

Le paramétrage suivant reproduit au plus près le paramétrage par défaut de JUL, dans Tomcat 6. J’insiste sur le fait que cette configuration n’est pas idéale dans Log4J et que pour un environnement de production, il est préférable de ne pas utiliser les conversions %C et %M. De même, il est faut privilégier les formats de dates prédéfinis, comme %d{DATE}.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="link:http://jakarta.apache.org/log4j/[http://jakarta.apache.org/log4j/]">

  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
    <param name="threshold" value="DEBUG"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{d MMM yyyy hh:mm:ss} %C %M%n%5p %m%n"/>
    </layout>
  </appender>

  <appender name="CATALINA" class="org.apache.log4j.rolling.RollingFileAppender">
    <param name="threshold" value="DEBUG"/>
    <rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
      <param name="FileNamePattern" value="${catalina.base}/logs/catalina.%d{yyyy-MM-dd}.log"/>
    </rollingPolicy>

    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{d MMM yyyy hh:mm:ss} %C %M%n%5p %m%n"/>
    </layout>
  </appender>

  <appender name="LOCALHOST" class="org.apache.log4j.rolling.RollingFileAppender">
    <param name="threshold" value="DEBUG"/>
    <rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
      <param name="FileNamePattern" value="${catalina.base}/logs/localhost.%d{yyyy-MM-dd}.log"/>
    </rollingPolicy>

    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{d MMM yyyy hh:mm:ss} %C %M%n%5p %m%n"/>
    </layout>
  </appender>

  <appender name="MANAGER" class="org.apache.log4j.rolling.RollingFileAppender">
    <param name="threshold" value="DEBUG"/>
    <rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
      <param name="FileNamePattern" value="${catalina.base}/logs/manager.%d{yyyy-MM-dd}.log"/>
    </rollingPolicy>

    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{d MMM yyyy hh:mm:ss} %C %M%n%5p %m%n"/>
    </layout>
  </appender>

  <appender name="ADMIN" class="org.apache.log4j.rolling.RollingFileAppender">
    <param name="threshold" value="DEBUG"/>
    <rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
      <param name="FileNamePattern" value="${catalina.base}/logs/admin.%d{yyyy-MM-dd}.log"/>
    </rollingPolicy>

    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{d MMM yyyy hh:mm:ss} %C %M%n%5p %m%n"/>
    </layout>
  </appender>

  <appender name="HOST-MANAGER" class="org.apache.log4j.rolling.RollingFileAppender">
    <param name="threshold" value="DEBUG"/>
    <rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
      <param name="FileNamePattern" value="${catalina.base}/logs/host-manager.%d{yyyy-MM-dd}.log"/>
    </rollingPolicy>

    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{d MMM yyyy hh:mm:ss} %C %M%n%5p %m%n"/>
    </layout>
  </appender>

  <logger name="org.apache.catalina.core.ContainerBase.[Catalina].[localhost]" additivity="false">
    <level value="INFO"/>
    <appender-ref ref="LOCALHOST"/>
  </logger>

  <logger name="org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager]" additivity="false">
    <level value="INFO"/>
    <appender-ref ref="MANAGER"/>
  </logger>

  <logger name="org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/admin]" additivity="false">
    <level value="INFO"/>
    <appender-ref ref="ADMIN"/>
  </logger>

  <logger name="org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager]" additivity="false">
    <level value="INFO"/>
    <appender-ref ref="HOST-MANAGER"/>
  </logger>

  <root>
    <level value="INFO"/>
    <appender-ref ref="CONSOLE" />
    <appender-ref ref="CATALINA"/>
  </root>
</log4j:configuration>

Log4J et SecurityManager

Si on démarre Tomcat en mode security, Log4J ne peut pas être initialisé à cause d’un manque de permission.

 ./startup.sh -security

Il faut donc modifier le fichier conf/catalina.policy et l’adapter aux besoin de Log4J. La configuration diffère légèrement entre une configuration par fichier properties et celle en fichier xml.

Configuration par properties

La modification de permissions porte sur le fichier tomcat-juli.jar. Il faut avant tout que ce fichier puisse lire le fichier de configuration de Log4J et les propriétés de type "log4j.*". Le reste des permissions dépend de cette configuration ; si, de façon classique, les traces sont envoyés dans des fichiers du répertoire logs, il faut prévoir les permissions correspondantes.

grant codeBase "file:${catalina.home}/bin/tomcat-juli.jar" {
  permission java.util.PropertyPermission "", "read";
  permission java.io.FilePermission "${catalina.base}${file.separator}lib${file.separator}log4j.properties", "read";
  permission java.io.FilePermission "${catalina.base}${file.separator}logs", "read, write";
  permission java.io.FilePermission "${catalina.base}${file.separator}logs${file.separator}", "read, write";
};

Configuration par XML

Le changement de nom du fichier ne suffit pas tout à fait pour que cela fonctionne. Si le fichier est bien pris en compte, et les traces bien générées, il reste des alertes au moment de l’initialisation de Log4J.

grant codeBase "file:${catalina.home}/bin/tomcat-juli.jar" {
  permission java.io.FilePermission "${catalina.base}${file.separator}lib${file.separator}log4j.xml", "read";
  permission java.io.FilePermission "${catalina.base}${file.separator}logs", "read, write";
  permission java.io.FilePermission "${catalina.base}${file.separator}logs${file.separator}*", "read, write";
};

L’ajout de la permission suivante permet un bon fonctionnement, même si elle est un peu radicale :

  permission java.io.FilePermission "<<ALL FILES>>", "read";