Home » Enterprise Java » Logback » Logback Custom Appender Example

About Rajagopal ParthaSarathi

Rajagopal works in software industry solving enterprise-scale problems for customers across geographies specializing in open source distributed platforms. He currently holds masters in computer science with focus on cloud computing from Illinois Institute of Technology. His current interests include but not limited to machine learning and big data engineering.

Logback Custom Appender Example

This article discusses creating a custom Appender for logback, a logging framework for the Java application.

1. Introduction to Logback

Logback is designed to be the successor for Log4j. It has been developed by the same development community. These are some of the advantages logback has over log4j

  • Faster implementation – ~10x faster on some critical areas
  • Automatic Reloading of configuration files
  • Ability to configure in groovy
  • Gracefully recover from I/O failures
  • Conditional processing of configuration files
  • Native Support for SLF4J

SLF4J is expanded as Simple Logging Facade for Java. It provides a logging facade to Java applications enabling the option to switch out logging framework. Currently, it supports Log4J, Logback and java.util.logging.

Logback uses Appenders to write to the logging destination. Appender has configurable properties which can be used to fine-tune it and also supply the logging destination.

2. Appenders

SLF4J provides a Logger interface which must be implemented by concrete implementation. Logback provides a concrete implementation for the Logger interface specified in SLF4J. Logback implementation of logging various levels use appenders to log the event to a specified destination.

In Logback, all Appenders must implement the Appender interface which specifies that doAppend method must be implemented along with setting a name for the appender. The destination also depends on the appender being used. For convenience, two abstract implementations of appender have been provided in Logback.

  • AppenderBase – It provides basic functionality such as getting or setting Appender name, activation status, layout and filters.
  • UnsynchronizedAppenderBase – This is similar to AppenderBase except that it does not handle thread synchronization and the Appender class extending must handle it, if needed.

In this section, we will take a look at an existing appender with the help of a simple project. The first step is to look at the gradle file used to manage the project.

build.gradle
apply plugin: 'java'
apply plugin: 'idea'
group = 'com.jcg'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
    mavenCentral()
}
dependencies {
    compile 'ch.qos.logback:logback-classic:1.2.3'
    compile 'org.slf4j:slf4j-api:1.7.25'
}
  • In lines 1-2, we specify plugins as java and idea to indicate we are running IntelliJ Idea Java project. Eclipse plugin can be used in place of idea.
  • We specify group, version information along with sourceCompatibility to indicate the Java version of 1.8.
  • We use mavenCentral as the repository to fetch dependencies to be used.
  • The dependencies for our project are just logback and slf4j.
logback.xml
<configuration>
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <target>System.out</target>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="console"/>
    </root>

</configuration>
  • An instance of ConsoleAppender is created with target as System.out. We can also use System.err as the destination.
  • An encoder with layout (PatternLayout) specified to affix logs with current time and thread executing it.
  • Logger has been specified with info level and reference to console appender is provided.
LoggerRoot.java
public class LoggerRoot {
    private static final Logger logger = LoggerFactory.getLogger(LoggerRoot.class.getSimpleName());
    public static void main(String... args){
        IntStream.rangeClosed(1,10).forEach(counter->{
            logger.info("Counter:" + counter);
        });
    }
}
  • This is a simple application to log the counter to the console.
  • It runs the for loop from 1 to 10 and prints the number to console. The output is visible in the screenshot attached below.
Logback Custom Appender - Log Example
Log Example

3. Custom Appender

In this section, let us extend the previous example with our own custom appender. We will create a custom Appender to store the log messages in a concurrent map similar to an in-memory database. To store the concurrent map, we will create a MapHolder singleton class.

MapHolder.java
public class MapHolder {
    private Map eventMap = new ConcurrentHashMap();;
    private MapHolder(){}

    private static MapHolder MAP_INSTANCE = null;

    public static MapHolder create(){
        if(MAP_INSTANCE == null){
            MAP_INSTANCE = new MapHolder();
        }
        return MAP_INSTANCE;
    }

    public void putEvent(String key,String value){
        eventMap.put(key,value);
    }

    public Map getEventMap(){
        return eventMap;
    }

}

  • The above class follows the singleton pattern and always create the same instance of the class on any invocation.
  • It has putEvent method to append events to the hashmap while getEvent is used to get the hashmap for usage.

In the section below, we will see the implementation of our Custom appender

MapAppender.java
public class MapAppender extends AppenderBase {
    MapHolder holder = MapHolder.create();
    @Override
    protected void append(LoggingEvent event) {
        holder.putEvent(String.valueOf(System.nanoTime()), event.getMessage());
    }
}
  • MapAppender extends AppenderBase enabling synchronization with sensible defaults provided by the AppenderBase class.
  • It has a holder variable to initialize the MapHolder class and stores the object inside its state.
  • It implements the append method which will be called by the logback logger at runtime to log the LoggingEvent.
  • append method just puts the event into the MapHolder class for storing the event in ConcurrentHashMap.
logback.xml
<configuration>
    ...
 <appender name="map" class="com.jcg.logbackappender.MapAppender">
    </appender>

    <root level="info">
        <appender-ref ref="console"/>
        <appender-ref ref="map"/>
    </root>

</configuration>
  • In the configuration, we create an instance of our new appender class.
  • This created instance is tied up to the root logger for logging the messages.

To verify the capture of log messages in the map, We can leverage the following code.

LoggerRoot.java
 MapHolder.create().getEventMap().values().forEach((value) -> {
            System.out.println(value);
        });
  • Since MapHolder is singleton, we always get the same instance.
  • The above code gets the eventMap and logs the events to console.
  • One interesting thing seen from the output below is that since it is HashMap, the order of the logs is not guaranteed. The key stores the timestamp and can be used to order the log events.
Log Output
Counter:2
Counter:3
Counter:9
Counter:8
Counter:1
Counter:6
Counter:5
Counter:4
Counter:7
Counter:10

4. Download the Source Code

Download
You can download the full source code of this example here: Logback Custom Appender Example
(+2 rating, 2 votes)
Start the discussion Views Tweet it!

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you our best selling eBooks for FREE!

 

1. JPA Mini Book

2. JVM Troubleshooting Guide

3. JUnit Tutorial for Unit Testing

4. Java Annotations Tutorial

5. Java Interview Questions

6. Spring Interview Questions

7. Android UI Design

 

and many more ....

 

Receive Java & Developer job alerts in your Area

 

Leave a Reply

avatar
  Subscribe  
Notify of