Logback

Logback Additivity Example

If you use Logback or Log4j logging framework, you may come across some situations that you realize too much or too little log messages on the console or in a file. But you don’t actually understand how it happens. It is probably the consequence of the additivity attribute in the logging framework. So in this post, we shall discuss the additivity flag in the Logback framework.

1. Logback Framework

What is Logback? Logback is intended as a successor to the popular Log4j project. After broad experiences and feedbacks, Log4j’s founder, Ceki Gülcü also designed the Logback as a most popular and modern logging framework. You can find more information about Logback architecture and Logback configuration in one of my posts: Logback Syslog Example. In this part, I would like to discuss Logback named hierarchy and additivity.

But before going into detail, we should be sure that we are already familiar with Logger and Appender classes in the Logback. Briefly, loggers are logical, own-configurable components in the framework. Each log event for a given logger is forwarded to the relevant appender. Appender determines the log destination system such as console, file, e-mail, syslog…

1.1 Logback Named Hierarchy

In the Logback, Loggers have case-sensitive names and they follow the hierarchical naming rule: A logger is said to be an ancestor of another logger if its name followed by a dot is a prefix of the descendant logger name. A logger is said to be a parent of a child logger if there are no ancestors between itself and the descendant logger. For example, the logger named “com.javacodegeeks” is a parent of the logger named “com.javacodegeeks.example”. Similarly, “java” is a parent of “java.util” and an ancestor of “java.util.List”. The root logger resides at the top of the logger hierarchy.

1.2 Logback Additivity

Appenders are added to the loggers. One logger may include more than one appenders. Thus, its log messages are written more than one desired destination systems. Additivity is exactly about this point.

The output of a log statement of logger A will go to all the appenders in A and its ancestors. However, if an ancestor of logger A, say B, has the additivity flag set to false, then A’s output will be directed to all the appenders in A and its ancestors up to and including B but not the appenders in any of the ancestors of B. Loggers have their additivity flag set to true by default.

In the table below, I design a logger named hierarchy with different varieties of appenders, additivity flags. You can see the target output as the result of this configuration.

Logger NameAttached AppendersAdditivity FlagOutput TargetsDescription
rootA1not applicableA1The additivity flag does not apply to the root logger.
comA2, A3trueA1, A2, A3Appenders of “com” and of root.
com.javacodegeeksnonetrueA1, A2, A3Appenders of “com” and of root.
com.javacodegeeks.examplesA4trueA1, A2, A3, A4Appenders of “com.javacodegeeks.examples”, “com” and of root.
com.javacodegeeks.applicationsA5falseA5Additivity flag is false. So only appenders of “com.javacodegeeks.applications”
com.javacodegeeks.applications.javanonetrueA5Only appenders of “com.javacodegeeks.applications” because its additivity flag is set to false.
Table 1.

2. Overview

In the example, we design an employee hierarchy in a company: “Director”, “Manager”, “Engineer” and “Intern” respectively. We create individual classes that inherit Person class for both of them. Furthermore, we also create the same “employee hierarchy” in the Logback loggers and we configure the name of loggers as “employee.director”, “employee.director.manager”, “employee.director.manager.engineer” and “employee.director.manager.engineer.intern” respectively to present the ancestor-child relationship.

Our preferred IDE is Eclipse and preferred build automation tool is Maven. I have already illustrated in detail how to create a Maven Project in the Eclipse in my previous example. This post is also about Logback, so the maven dependencies are the same with this example. You can examine: Logback File Appender Example

3. Implementation

All types of the employee classes ( Director, Manager, Engineer, Intern ) extend the Employee super class. I guess, you are already familiar with this sample hierarchy if you had any object-oriented programming course. I put logger statements in their “constructor” methods to keep them simple and not to need to add extra methods. Logger instances are produced by their corresponding logger definition in the Logback configuration file ( logback.xml ). For example, The logger instance in the Manager class is produced by the “employee.directory.manager” logger definition.

Employee.java

package com.javacodegeeks.examples.logbackadditivityexample.model;

public class Employee {

	protected final String	name;

	public Employee( final String name ) {
		this.name = name;
	}
}

Director.java

package com.javacodegeeks.examples.logbackadditivityexample.model;

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

public class Director extends Employee {

	private static final Logger	LOGGER	= LoggerFactory.getLogger( "employee.director" );

	public Director( final String name ) {
		super( name );

		LOGGER.info( "New Director is created. His/her name is : {}", super.name );
	}
}

Manager.java

package com.javacodegeeks.examples.logbackadditivityexample.model;

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

public class Manager extends Employee {

	private static final Logger	LOGGER	= LoggerFactory.getLogger( "employee.director.manager" );

	public Manager( final String name ) {
		super( name );

		LOGGER.info( "New Manager is created. His/her name is : {}", super.name );
	}
}

Engineer.java

package com.javacodegeeks.examples.logbackadditivityexample.model;

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

public class Engineer extends Employee {

	private static final Logger	LOGGER	= LoggerFactory.getLogger( "employee.director.manager.engineer" );

	public Engineer( final String name ) {
		super( name );

		LOGGER.info( "New Engineer is created. His/her name is : {}", super.name );
	}
}

Intern.java

package com.javacodegeeks.examples.logbackadditivityexample.model;

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

public class Intern extends Employee {

	private static final Logger	LOGGER	= LoggerFactory.getLogger( "employee.director.manager.engineer.intern" );

	public Intern( final String name ) {
		super( name );

		LOGGER.info( "New Intern is created. His/her name is : {}", super.name );
	}
}

To get the logger statements run, we need a main method. Simply, we instantiate the Employee objects in order to execute their constructors that include logger statements.

ApplicationStarter.java

package com.javacodegeeks.examples.logbackadditivityexample;

import com.javacodegeeks.examples.logbackadditivityexample.model.Director;
import com.javacodegeeks.examples.logbackadditivityexample.model.Employee;
import com.javacodegeeks.examples.logbackadditivityexample.model.Engineer;
import com.javacodegeeks.examples.logbackadditivityexample.model.Intern;
import com.javacodegeeks.examples.logbackadditivityexample.model.Manager;

public class ApplicationStarter {

	@SuppressWarnings( "unused" )
	public static void main( final String[] args ) {

		final Employee director = new Director( "Ali" );

		final Employee manager = new Manager( "Susan" );

		final Employee engineer = new Engineer( "Abony" );

		final Employee intern = new Intern( "Mehmet" );
	}
}

In the Logback configuration file, we define 5 file appenders and one console appender. We attach these appenders to the named loggers according to the employee hierarchy and root logger. Please notice that we set additivity flag of the “employee.director.manager” and “employee.director.manager.engineer.intern” named loggers to false. It means that their log messages are not inserted into the log appenders of their ancestor loggers.

logback.xml

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

	<property name="LOG_HOME" value="c:/logs/" />
	
	<appender name="FILE1" class="ch.qos.logback.core.FileAppender">
		<file>${LOG_HOME}/log1.log</file>
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<Pattern>
				%-5level %logger{36} - %msg%n
			</Pattern>
		</encoder>		
	</appender>
	
	<appender name="FILE2" class="ch.qos.logback.core.FileAppender">
		<file>${LOG_HOME}/log2.log</file>
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<Pattern>
				%-5level %logger{36} - %msg%n
			</Pattern>
		</encoder>		
	</appender>
	
	<appender name="FILE3" class="ch.qos.logback.core.FileAppender">
		<file>${LOG_HOME}/log3.log</file>
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<Pattern>
				%-5level %logger{36} - %msg%n
			</Pattern>
		</encoder>		
	</appender>
	
	<appender name="FILE4" class="ch.qos.logback.core.FileAppender">
		<file>${LOG_HOME}/log4.log</file>
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<Pattern>
				%-5level %logger{36} - %msg%n
			</Pattern>
		</encoder>		
	</appender>
	
	<appender name="FILE5" class="ch.qos.logback.core.FileAppender">
		<file>${LOG_HOME}/log5.log</file>
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<Pattern>
				%-5level %logger{36} - %msg%n
			</Pattern>
		</encoder>		
	</appender>

	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
		<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
			<Pattern>
				%-5level %logger{36} - %msg%n
			</Pattern>
		</encoder>		
 	</appender>
 	  	
	<root level="INFO">
		<appender-ref ref="FILE1" />
		<appender-ref ref="STDOUT" />
  	</root>
  	  		
	<logger name="employee.director" level="INFO">
		<appender-ref ref="FILE2" />
	</logger>
	
	<logger name="employee.director.manager" level="INFO" additivity="false">
		<appender-ref ref="FILE3" />
	</logger>
	
	<logger name="employee.director.manager.engineer" level="INFO">
		<appender-ref ref="FILE4" />
	</logger>
	
	<logger name="employee.director.manager.engineer.intern" level="INFO" additivity="false">
		<appender-ref ref="FILE5" />
	</logger>

</configuration>

By this configuration, we expect the output targets of the loggers as below:

Logger NameAttached AppendersAdditivity FlagOutput Targets
rootFILE1, STDOUTnot applicableFILE1,STDOUT
employee.directorFILE2true by defaultFILE1, STDOUT, FILE2
employee.director.managerFILE3falseFILE3
employee.director.manager.engineerFILE4true by defaultFILE3, FILE4
employee.director.manager.engineer.internFILE5falseFILE5

After execution, we obtain these results in the destination files and the console:

Console:

INFO  employee.director - New Director is created. His/her name is : Ali

file1.log’s content:

INFO  employee.director - New Director is created. His/her name is : Ali

file2.log’s content:

INFO  employee.director - New Director is created. His/her name is : Ali

file3.log’s content:

INFO  employee.director.manager - New Manager is created. His/her name is : Susan
INFO  employee.director.manager.engineer - New Engineer is created. His/her name is : Abony

file4.log’s content:

INFO  employee.director.manager.engineer - New Engineer is created. His/her name is : Abony

file5.log’s content:

INFO  e.director.manager.engineer.intern - New Intern is created. His/her name is : Mehmet

Now let’s discuss the results and criticize each statement in the ApplicationStarter class:

The statement final Employee director = new Director( "Ali" ); gets the named logger employee.director run. FILE2 appender is attached to it. So the log message ( “New Director is created. His/her name is : Ali” ) in the Director class is written to the “file2.log” file. Its additivity flag is set to true by default. Thus, the log message is also added to the appenders ( console and “file1.log” file ) of the root logger. Therefore, we surely see the same log message in the console, “file1.log” file and “file2.log” file.

The statement final Employee manager = new Manager( "Susan" ); gets the named logger employee.director.manager run. FILE3 appender is attached to it. So the log message ( “New Manager is created. His/her name is : Susan” ) in the Manager class is written to the “file3.log” file. Its additivity flag is set to false. Thus, this message is not added to the appenders of the ancestor loggers ( “employee.director” named logger and root logger ) of the “employee.director.manager” named logger. Otherwise we see the message in file2, file1 and console.

The statement final Employee engineer = new Engineer( "Abony" ); gets the named logger employee.director.manager.engineer run. FILE4 appender is attached to it. So the log message ( “New Engineer is created. His/her name is : Abony” ) in the Engineer class is written to the “file4.log” file. Its additivity flag is set to true by default. So the same message is added to the appenders ( “file3.log” file ) of its ancestor logger ( “employee.director.manager” named logger ). Please note that this message is also not added to the ancestor logger appenders higher than the “employee.director.manager” named logger. Because “employee.director.manager” named logger’s additivity flag is false. Log messages are written to the appenders of the ancestor loggers respectively until root logger or a additivity “false” logger in the hierarchy is encountered.

The statement final Employee intern = new Intern( "Mehmet" ); gets the named logger employee.director.manager.engineer.intern run. FILE5 appender is attached to it. So the log message ( “New Intern is created. His/her name is : Mehmet” ) in the Intern class is written to the “file5.log” file. Its additivity flag is set to false. Thus, this message is not added to the appenders of the ancestor loggers.

4. Download the Eclipse Project

This project demonstrates how to use the “additivity flag” in the Logback framework. Download link is below.

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

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