Buscar este blog

sábado, 1 de agosto de 2015

Externalize log4j configuration file in webapp

Note: This solution was updated using spring in http://trabajosdesisifo.blogspot.com.es/2015/11/externalize-log4j-configuration-file-in.html


All web application needs a Log. With the information written in a log file you can audit your application, check its health and trace problems.

Log4j is just one framework you can use to generate logs. You configure log behaviour by using a configuration file: log4j.xml or log4j.properties.
Usually this files are stored inside your app, i.e packed inside your war. This implies that you will have the same logging options in all of the environments: development, test, preproduction and production.

This could be a simple example of a log4j.xml file:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <!-- ************************************************ -->
    <!-- ****************** APPENDERS ******************* -->
    <!-- ************************************************ -->
    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
  <layout class="org.apache.log4j.PatternLayout">
   <param name="ConversionPattern" value="[%d] (%F\:%L) %p %t %c %m%n" />
  </layout>
 </appender>

 <appender name="LOG" class="org.apache.log4j.DailyRollingFileAppender">
  <param name="File" value="/var/log/jboss/myApp.log" />
  <param name="DatePattern" value="'.'yyyy-MM-dd"/>
  <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%-5p][%d{ISO8601}][%-4L] %F>%m%n" />
        </layout>
 </appender>
    
    <!-- ************************************************ -->
    <!-- ****************** LOGGERS ********************* -->
    <!-- ************************************************ -->
    <logger name="es.sisifo.package1">
        <level value="DEBUG" />        
    </logger>
    
 <logger name="es.sisifo.package2">
        <level value="DEBUG" />     
    </logger>
 
    <logger name="org.springframework">
        <level value="DEBUG" />  
    </logger>

 <logger name="es.sisifo">
  <level value="INFO" />
 </logger>

 <root>
  <priority value="WARN" />
  <appender-ref ref="CONSOLE" />
  <appender-ref ref="LOG" />
 </root>
</log4j:configuration>

Here you have two packages of your app plus all spring classes in DEBUG level, the rest of your app in INFO and the rest of the classes in WARN.
A file called myApp.log is generated in /var/log/jboss directory.


You can use environment properties inside configuration files in order to externalize some parameters, for example, the path in which files are written. Also, you can use system properties passed to the JVM in boot time.
The previous log4j.xml config file would be this:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <!-- ************************************************ -->
    <!-- ****************** APPENDERS ******************* -->
    <!-- ************************************************ -->
    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
  <layout class="org.apache.log4j.PatternLayout">
   <param name="ConversionPattern" value="[%d] (%F\:%L) %p %t %c %m%n" />
  </layout>
 </appender>

 <appender name="LOG" class="org.apache.log4j.DailyRollingFileAppender">
  <param name="File" value="${jboss.server.log.dir}/jboss/myApp.log" />
  <param name="DatePattern" value="'.'yyyy-MM-dd"/>
  <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%-5p][%d{ISO8601}][%-4L] %F>%m%n" />
        </layout>
 </appender>
    
    <!-- ************************************************ -->
    <!-- ****************** LOGGERS ********************* -->
    <!-- ************************************************ -->
    <logger name="es.sisifo.package1">
        <level value="DEBUG" />        
    </logger>
    
 <logger name="es.sisifo.package2">
        <level value="DEBUG" />     
    </logger>
 
    <logger name="org.springframework">
        <level value="DEBUG" />  
    </logger>

 <logger name="es.sisifo">
  <level value="INFO" />
 </logger>

 <root>
  <priority value="${system.myApp.defaultLogLevel}" />
  <appender-ref ref="CONSOLE" />
  <appender-ref ref="LOG" />
 </root>
</log4j:configuration>

Note that "jboss.server.log.dir" is a well-known JBoss property and "system.myApp.defaultLogLevel" would be a custom system property.

But imagine you have your app in production and some strange behaviour suddenly  appears in a apache-commons class. You would like to add a new logger tag and put this package in DEBUG in order to obtain more information. In this scenario, you would need to deploy a new version of your app with a modified log4j.xml.
The solution is to externalize the whole configuration file, and to force your app to read it in boot time. In this way you simple need to change the file and restart the app..


To externalize configuration file you have Log4jConfigListener. This class allow you to specify a external location of this file and load it when the app starts. In your web.xml you must set a context param called log4jConfigLocation that points to this file. This param could be expressed in multiples ways:
  • classpath:/... in order to indicate a custom path to the file inside your classpath.
  • file:... in order to indicate a external path to the file
  • ${} in order to indicate a system property whose value contains the path to the external file
For example:
<!-- Configuración de log -->
 <context-param>
  <param-name>log4jConfigLocation</param-name>
  <param-value>${system.myApp.log4jConfigLocation.web:classpath:/internal_log4j.xml}</param-value>
 </context-param>

 <context-param>
  <param-name>log4jExposeWebAppRoot</param-name>
  <param-value>false</param-value>
 </context-param>

 <listener>
  <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
 </listener>

Note that ${} accepts a default value expressed after the ":". In this example, the file would be allocated in the path expressed by "system.myApp.log4jConfigLocation.web" system property and, if this property does not exists, the default value would be "classpath:/internal_log4j.xml" (inside classpath).
In this example, de value of system.myApp.log4jConfigLocation.web would be "file:/home/jboss/myAppConfig/log4j-custom.xml" (note de "file:" prefix).

In JBoss you can either set system properties through web admin console (localhost:9990) in standalone or domain modes (in server-groups). In domain mode, the best way is to set the property in the server-group, keeping in mind that all servers share the same path.

When app starts you should see this log:
[Server:server-full-ha-1] 12:42:12,235 INFO  [org.springframework.beans.factory.config.PropertiesFactoryBean] (ServerService Thread Pool -- 107) Loading properties file from URL [file:/home/jboss/myAppConfig/log4j-custom.xml]

No hay comentarios:

Publicar un comentario