Logback

Logback File Appender Example

Logging is a common and essential issue for software development. Logging allows you to analyse the program execution flow, to detect the bugs and warnings in the code. So logs are frequently the best (and sometimes the only) source of information about a running program.

In this example, we are going to show you how to record log messages in files using Logback framework. After a brief introduction to the overall Logback framework and File Appender in the Logback, we will discuss the implementation details of our example.

1. What is Logback?

Logback is the most recent and modern logging framework in the Java enterprise world. It gained the decade of experience in the logging systems. Log4j, Java logging API, Apache Commons Logging are some other alternatives. As a successor to the popular Log4j project, Logback is also designed by Ceki Gülcü, Log4j’s founder.

1.1 Logback Architecture

Logback is divided into three modules, logback-core, logback-classic and logback-access. The core module provides the groundwork for the other two modules. The classic module corresponds to a significantly improved version of Log4j. The logback-access module integrates with Servlet containers, such as Tomcat and Jetty, to provide rich and powerful HTTP-access log functionality. But this module is out of scope in our example.

It is a good practice to write the log statements with SLF4J API that enables simple abstraction for various logging frameworks. At runtime, SLF4J codes are bound to the preferred logging framework in the classpath. Logback-classic module natively implements the SLF4J API. So this flexible architecture allows the end user to switch back and forth between other logging systems and Logback.

1.2 Logback Configuration

Logback can be configured either programmatically or with a configuration file expressed in XML or Groovy format. Logback follows these steps to try to configure itself:

1) Logback tries to find a file called logback.groovy in the classpath.
2) If no such file is found, logback tries to find a file called logback-test.xml in the classpath.
3) If no such file is found, it checks for the file logback.xml in the classpath.
4) If neither file is found, logback configures itself automatically using the BasicConfigurator which will cause logging output to be directed to the console.

Each log event for a given logger is forwarded to the relevant appender. Appenders are responsible for writing the event data to the target destination system such as console, file, e-mail, syslog. In our example, we use file appender in order to forward the log messages to the files.

1.3 File Appender in the Logback

File Appender which is the major topic of this example appends log events into a file. We display the file appender properties in a table below and explain them in a nutshell:

Property NameDescription
appendIf this boolean type property is true, messages are appended at the end of an existing file. Otherwise, any existing file is truncated. Default append option is true.
fileIt indicates the name of the file to write to. If the file does not exist, it is created. If the parent directory of the file does not exist, FileAppender will automatically create it, including any necessary but nonexistent parent directories.
encoderDetermines the behaviour in which an event is written to the underlying OutputStreamAppender. At the present time, PatternLayoutEncoder is the only really useful encoder. Pattern layouts express the log message format with some fields such as length, thread name, log level… In previous versions of Logback, PatternLayout is nested within a FileAppender. Since Logback 0.9.19, FileAppender and sub-classes expect an encoder and no longer take a layout.
prudentIf the value of this boolean type option is true, logs are appended in prudent mode. The prudent mode in Logback serializes IO operations between all JVMs writing to the same file, potentially running on different hosts. Thus, it provides safely writing to the specified file with file locks. Default option is false.

2. Overview

We design a simple printer interface. Consider a print method of the interface that accepts two parameters: A message to print and message id number. Think of an exceptional case: When the message id number is divisible by three, the print method of the Printer class always throw an error.

In the main method of the application, we invoke the print method of the Printer class in a loop from one to ten. We send loop counter as a message id number. As a result, we expect to get exceptions when the method is called with message id numbers of 3, 6 and 9 which are divisible by three. Thereby this scenario, we can illustrate logging errors in the exceptional situations.

Tip
You may skip project creation and jump directly to the beginning of the example below.

3. Create a new Maven project

Go to File -> New -> Project ->Maven -> Maven Project.

Eclipse new Project Wizard
Eclipse new Project Wizard

In the next screen, accept the default options and click on Next

Eclipse Maven Project
Eclipse Maven Project

In the next screen, select the maven-archetype-quickstart option and click Next

Eclipse Maven Project
Eclipse Maven Project

In the next screen, type the Group Id, Artifact Id and Package, as in the following screen and click on Finish

Eclipse Maven Project
Eclipse Maven Project

As you see, the project is created in your workspace.

Eclipse Project
Eclipse Project

3.1 Adding Maven dependencies

Before we execute some code, we need to add logback dependencies in the Maven’s pom.xml file. It is enough to add only logback-classic artifact. Logback-core and slf4j-api packages are transitive dependencies of the logback-classic artifact. By the power of the Maven Dependency Management, they are automatically added to the classpath. We would like to show groovy-based configuration, so we add the groovy package in the pom.xml.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.javacodegeeks.examples</groupId>
  <artifactId>logbackfileappenderexample</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>logbackfileappenderexample</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.2</version>
				<configuration>
					<source>1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>
		</plugins>
	</build>

	<dependencies>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.1.3</version>
		</dependency>
		<dependency>
    		<groupId>org.codehaus.groovy</groupId>
    		<artifactId>groovy</artifactId>
    		<version>2.4.3</version>
		</dependency>
	</dependencies>
	    
</project>

4. Implementation

In this example, we configure the Logback with Groovy script. We are creating two file appenders: One of them is for audit reports that includes all of the log messages, the other one is only for error logs. Audit log messages file path is c:/logs/printerdebug.log, error logs file path is c:/logs/printererror.log. As a good practice, we may keep the error logs in a different private file in order to explore straight when a code defect is reported to us.

logback.groovy

import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.FileAppender

def logHomeDirectory = "c:/logs/printer"

appender("AUDIT_FILE", FileAppender) {
	file = "${logHomeDirectory}debug.log"
	encoder(PatternLayoutEncoder) { pattern = "%-5level %logger{36} - %msg%n" }
}

appender("ERROR_FILE", FileAppender) {
	file = "${logHomeDirectory}error.log"
	encoder(PatternLayoutEncoder) { pattern = "%-5level %logger{36} - %msg%n" }
}

logger("com.javacodegeeks.examples.logbackfileappenderexample.exception", ERROR , ["ERROR_FILE"])

logger("com.javacodegeeks.examples.logbackfileappenderexample", DEBUG , ["AUDIT_FILE"])

Groovy script is not used widely as xml configuration. So if you intend to prefer xml configuration, the xml equivalent of the groovy script is below. But please note that as we explain in the configuration section, logback.groovy file has higher priority than logback.xml file. Logback takes into account the logback.groovy file configuration, if both of the them are in the classpath of the code.

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

	<property name="LOG_HOME" value="c:/logs/printer" />
	
	<appender name="AUDIT_FILE" class="ch.qos.logback.core.FileAppender">
		<file>${LOG_HOME}debug.log</file>
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<Pattern>
				%-5level %logger{36} - %msg%n
			</Pattern>
		</encoder>		
	</appender>
	
	<appender name="ERROR_FILE" class="ch.qos.logback.core.FileAppender">
		<file>${LOG_HOME}error.log</file>
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<Pattern>
				%-5level %logger{36} - %msg%n
			</Pattern>
		</encoder>		
	</appender>

	<logger name="com.javacodegeeks.examples.logbackfileappenderexample.exception" level="ERROR">
		<appender-ref ref="ERROR_FILE" />
	</logger>

	<logger name="com.javacodegeeks.examples.logbackfileappenderexample" level="DEBUG">
		<appender-ref ref="AUDIT_FILE" />
	</logger>

</configuration>

We create a custom Exception class for our special “divide by three” error case. We write error level log, when this exception occurs.

PrinterDivideByThreeException.java

package com.javacodegeeks.examples.logbackfileappenderexample.exception;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PrinterDivideByThreeException extends Exception {

	private static final Logger	LOGGER				= LoggerFactory.getLogger( PrinterDivideByThreeException.class );

	private static final long	serialVersionUID	= 445670554417085824L;

	public PrinterDivideByThreeException( final String message, final int id ) {
		super( message );

		LOGGER.error( "Printing was failed. Message id : {}, Error message: {}", id, message );
	}
}

In the print method of our Printer class, we create logs in debug level for every call at the beginning of the method. Then we check whether the message id number is divisible by three. If so, we throw our custom Exception, furthermore we provide the logging in error level. If the message id number is not divisible by three, we follow with info logging and return “success”.

Printer.java


package com.javacodegeeks.examples.logbackfileappenderexample;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.javacodegeeks.examples.logbackfileappenderexample.exception.PrinterDivideByThreeException;

public class Printer {

	private static final Logger	LOGGER	= LoggerFactory.getLogger( Printer.class );

	public String print( final String message, final int id ) throws PrinterDivideByThreeException {

		LOGGER.debug( "Message was received to print. Message : {}, Message id : {}", message, id );

		// If the message id is divisible by three, then throw exception.
		if ( id % 3 == 0 ) {
			throw new PrinterDivideByThreeException( "Message id can not be divided by three", id );
		}

		LOGGER.info( "Printing is success. Message id : {}", id );

		return "success";
	}
}

In the ApplicationStarter class that holds the main method, we invoke the print interface ten times in a for loop.

ApplicationStarter.java


package com.javacodegeeks.examples.logbackfileappenderexample;

import com.javacodegeeks.examples.logbackfileappenderexample.exception.PrinterDivideByThreeException;

public class ApplicationStarter {

	public static void main( final String[] args ) {

		final Printer printer = new Printer();

		// Send ten messages
		for ( int i = 1; i <= 10; i++ ) {

			try {
				printer.print( "Message" + i, i );
			} catch ( final PrinterDivideByThreeException e ) {

			}
		}
	}
}

When the execution of the application is completed, we obtain the two log files as below.

printerdebug.log

DEBUG c.j.e.l.Printer - Message was received to print. Message : Message1, Message id : 1
INFO  c.j.e.l.Printer - Printing is success. Message id : 1
DEBUG c.j.e.l.Printer - Message was received to print. Message : Message2, Message id : 2
INFO  c.j.e.l.Printer - Printing is success. Message id : 2
DEBUG c.j.e.l.Printer - Message was received to print. Message : Message3, Message id : 3
ERROR c.j.e.l.exception.PrinterException - Printing was failed. Message id : 3, Error message: Message id can not be divided by three
DEBUG c.j.e.l.Printer - Message was received to print. Message : Message4, Message id : 4
INFO  c.j.e.l.Printer - Printing is success. Message id : 4
DEBUG c.j.e.l.Printer - Message was received to print. Message : Message5, Message id : 5
INFO  c.j.e.l.Printer - Printing is success. Message id : 5
DEBUG c.j.e.l.Printer - Message was received to print. Message : Message6, Message id : 6
ERROR c.j.e.l.exception.PrinterException - Printing was failed. Message id : 6, Error message: Message id can not be divided by three
DEBUG c.j.e.l.Printer - Message was received to print. Message : Message7, Message id : 7
INFO  c.j.e.l.Printer - Printing is success. Message id : 7
DEBUG c.j.e.l.Printer - Message was received to print. Message : Message8, Message id : 8
INFO  c.j.e.l.Printer - Printing is success. Message id : 8
DEBUG c.j.e.l.Printer - Message was received to print. Message : Message9, Message id : 9
ERROR c.j.e.l.exception.PrinterException - Printing was failed. Message id : 9, Error message: Message id can not be divided by three
DEBUG c.j.e.l.Printer - Message was received to print. Message : Message10, Message id : 10
INFO  c.j.e.l.Printer - Printing is success. Message id : 10

printererror.log

ERROR c.j.e.l.exception.PrinterException - Printing was failed. Message id : 3, Error message: Message id can not be divided by three
ERROR c.j.e.l.exception.PrinterException - Printing was failed. Message id : 6, Error message: Message id can not be divided by three
ERROR c.j.e.l.exception.PrinterException - Printing was failed. Message id : 9, Error message: Message id can not be divided by three

5. Download the Eclipse Project

This project demonstrates how to log messages in the files using Logback.

Download
You can download the full source code of this example here : logbackfileappenderexample

Ilker Konar

I am a senior software developer with experience mostly in java-related technologies with appliance in the telecommunication industry. I have been programming for more than fifteen years. I am passionate about programming. I like learning new frameworks, languages and design patterns.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button