Logging with logback and external configuration

Logs and logging in general is the number one friend of a developer when it comes to troubleshooting in a live environment. Changing logging level on the fly is very handy in situations that something has gone wrong. In this post we will configure logback in order to have different log level per environment and also to be able to change it on the fly without the need of re-deployments or restart neither our app or our application server.

We will use a .properties file that will define the current environment and the path of an external logback.xml that will have the configuration which will be used only for the production environment. All these will be used by the traditional in-app logback.xml.

In order to succeed this we will use three features of logback. The Automatically reloading configuration file upon modification the File inclusion and the Conditional processing of configuration files.

Be aware that for those three features to work combined, we will need to have at least the 0.9.28 logback version and also to include Janino library in our application, otherwise our setup will fail to work.

In order to achieve the aforementioned behavior we will create a main logback.xml, a logback-dev.xml, a logback-prod.xml and a logback.properties file. The latter, will contain the information regarding the environment that will affect the logging configuration per environment. It is proposed to put the logback.properties file inside our server’s classpath so that it can be independent from our application(s). The logback.xml, that will be inside our deployable, will fetch this information and based on that it will use either the logback-dev.xml or the logback-prod.xml. Now in order to be able to edit the logback-prod.xml whenever we want, we will put it somewhere outside our deployable and inside our server. The logback-dev.xml could be inside our deployable, we don’t really care about that since it will be only accessed in development environments. The logback.properties file will also contain the root path were we can find the logback-prod.xml.

So taking into consideration all the above, here are the files we need:

logback.properties

mode=prod
logRoot=/opt/Oracle/Weblogic/user_projects/domains/myApp_domain

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
    Document   : logback.xml
    Created on : October 3, 2012, 10:18 AM
    Author     : leonidas patouchas
-->
<configuration scan="true" scanPeriod="300 seconds">
	<property resource="logback.properties" />

	<if condition='property("mode").equals("prod")'>
		<then>
			<include file="${logRoot}/myApp/logback-prod.xml" />
		</then>
	</if>
	<if condition='property("mode").equals("dev")'>
		<then>
			<include resource="logback-dev.xml" />
		</then>
	</if>
</configuration>

logback-dev.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
    Document   : logback-dev.xml
    Created on : October 3, 2012, 10:18 AM
    Author     : leonidas patouchas
    Description:
        Purpose of the document follows.
-->
<configuration>
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d %p [%c] - &lt;%m&gt;%n</Pattern>
        </layout>
    </appender>
    <!--Daily rolling file appender -->
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
          <File>${MYAPP_HOME}/nyApp.log</File>
          <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <FileNamePattern>gr-myApp.%d{yyyy-MM-dd}.log</FileNamePattern>
          <MaxHistory>2</MaxHistory>
          </rollingPolicy>
          <layout class="ch.qos.logback.classic.PatternLayout">
          <Pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</Pattern>
          </layout>
    </appender> 

    <logger name="org.springframework" level="INFO"/>
    <logger name="org.apache.openjpa" level="DEBUG"/>
    <logger name="gr" level="TRACE"/>

    <root level="TRACE">
        <appender-ref ref="stdout"/>
        <appender-ref ref="file" />
    </root>
</configuration>

logback-prod.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
    Document   : logback-prod.xml
    Created on : October 3, 2012, 10:18 AM
    Author     : leonidas patouchas
    Description:
        Purpose of the document follows.
-->
<configuration>
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d %p [%c] - &lt;%m&gt;%n</Pattern>
        </layout>
    </appender>
    <!--Daily rolling file appender -->
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
          <File>${MYAPP_HOME}/nyApp.log</File>
          <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <FileNamePattern>gr-myApp.%d{yyyy-MM-dd}.log</FileNamePattern>
          <MaxHistory>2</MaxHistory>
          </rollingPolicy>
          <layout class="ch.qos.logback.classic.PatternLayout">
          <Pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</Pattern>
          </layout>
    </appender> 

    <logger name="org.springframework" level="INFO"/>
    <logger name="org.apache.openjpa" level="DEBUG"/>
    <logger name="gr" level="ERROR"/>

    <root level="ERROR">
        <appender-ref ref="stdout"/>
        <appender-ref ref="file" />
    </root>
</configuration>

Note that the difference between the dev and the prod logback is only the loglevel. Of course we could do any kinds of changes to the configuration and have totally different logs per environment. In our case, the logback is pre-configured to check for updates in the logging configuration every 300 seconds. Be aware of performance issues if you decrease the refresh time to a few seconds.