Archived entries for Java

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.

Hibernate Mapping with Annotations

In the previous post we used the .hbm.xml to map our POJO with the DB table. There is another way we can do that, that does not involve any .xml file. Everything will be done in our POJO, so no extra files needed. That kind of implementation requires at least Java 5, hibernate 3.2.0 core and above and it is based on Annotations. We will use annotations to map our POJO.

The Persons.java class we used in the previous example (and was automatically generated by NetBeans) looked like this:

package gr.persons.entities;

import java.math.BigDecimal;
import java.util.Date;

public class Person  implements java.io.Serializable {

     private BigDecimal id;
     private String name;
     private String surname;
     private Date birthdate;
     private String sex;

    public Person() {
    }

    public Person(BigDecimal id) {
        this.id = id;
    }
    public Person(BigDecimal id, String name, String surname, Date birthdate, String sex) {
       this.id = id;
       this.name = name;
       this.surname = surname;
       this.birthdate = birthdate;
       this.sex = sex;
    }

    public BigDecimal getId() {
        return this.id;
    }

    public void setId(BigDecimal id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return this.surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public Date getBirthdate() {
        return this.birthdate;
    }

    public void setBirthdate(Date birthdate) {
        this.birthdate = birthdate;
    }

    public String getSex() {
        return this.sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

}

And we mapped id through the Persons.hbm.xml.

For the annotated solution we will have to alter a little bit the Persons.java:

package gr.persons.entities;

import java.math.BigDecimal;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name = "PERSON", schema = "YOUR_SCHEMA")
public class Person implements java.io.Serializable {

    @Id
    @Column(name = "ID", unique = true, nullable = false)
    private BigDecimal id;

    @Column(name = "NAME", length = 50)
    private String name;

    @Column(name = "SURNAME", length = 50)
    private String surname;

    @Temporal(TemporalType.DATE)
    @Column(name = "BIRTHDATE", length = 7)
    private Date birthdate;

    @Column(name = "SEX", length =10)
    private String sex;

    public Person() {
    }

    public Person(BigDecimal id) {
        this.id = id;
    }

    public Person(BigDecimal id, String name, String surname, Date birthdate, String sex) {
        this.id = id;
        this.name = name;
        this.surname = surname;
        this.birthdate = birthdate;
        this.sex = sex;
    }

    public BigDecimal getId() {
        return this.id;
    }

    public void setId(BigDecimal id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return this.surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public Date getBirthdate() {
        return this.birthdate;
    }

    public void setBirthdate(Date birthdate) {
        this.birthdate = birthdate;
    }

    public String getSex() {
        return this.sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

The implementation is pretty straight forward. With the annotation @Entity we defined that the Persons.java is an entity that represents the persistent object. With @Table we defined that the our Persons.java class is mapped with the table PERSONS in the schema YOUR_SCHEMA. We also defined the Primary Key that is the id using the @Id annotation. Finally we used the @Column annotation to map our DB columns.

Now in order for our changes to work we have to change our hibernate.cfg.xml. Instead of mapping the resource of Person.hbm.xml we will now map the class Persons.java:

Before:

<mapping resource="gr/persons/entities/Person.hbm.xml"/>

After:

<mapping class="gr.persons.entities.Person"/>

An other thing we must note is that in our HibernateUtil.java class we create our Session Factory based on AnnotationConfiguration.

sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();

This is needed for annotations to work. If we use Configuration, it will not work.

P.S Special thanks to SaperDuper that motivated me to write the annotated version of the previous tutorial :)



Copyright © 2010. Leonidas Patouchas. All rights reserved.

RSS Feed. This blog is proudly powered by Wordpress and uses Modern Clix, a theme by Rodrigo Galindez.