Logback Introduction: An Enterprise Logging Framework

Logback Introduction: An Enterprise Logging Framework

8 Comments

Monitoring, diagnosing, and troubleshooting are key activities in any enterprise application lifecycle, and logging is the core part of these activities. Through logging you get to see what the application code is actually doing during these activities at runtime. Using System.out to print messages to the console is simply not sufficient for enterprise applications. Enterprise applications have logging requirements with varied degree of complexities. You will need generating logs with different levels of importance, such as ERROR, WARN, INFO, and DEBUG. You’ll also have requirements to send logs to different destinations, such as console, file, database, SMTP server, or JMS queue destination. These requirements are not possible with simple System.out statements. Logging frameworks such as Logback are designed to meet the needs of logging in the enterprise.

Logback Architecture

Ceki Gülcü the founder of the Log4J along with Sébastien Pennec, another Log4J contributor, designed logback. With decades of experience with logging frameworks, they designed Logback to be fast and generic enough to work under different environments. Logback is comprised of three modules:

  • logback-core: Forms the foundation of logback-classic and logback-access. To perform logging, you need the more specialized logback-classic or logback-access.
  • logback-classic: Relies on logback-core for logging services.
  • logback-access: Provides HTTP-access log functionalities to servlets containers, such as Tomcat and Jetty.

In this post we will explore log-back-classic , which in going forward I’ll refer to as logback. Logback natively implements the Simple Logging Facade for Java (SLF4J) API. In a nutshell, SLF4J is a façade for various logging frameworks. As a developer, you’ll write logging code against the SLF4J API. At deployment time, you have the flexibility to plug-in a desired logging framework, made possible through an intermediate SLF4J bridge layer. As logback natively implements SLF4J, the additional SLF4J API layer doesn’t incur any performance overhead, a slight advantage that Logback has over other frameworks.

This figure illustrates the interaction of an application with Logback.

Interaction of a Java application with the Logback logging framework

The key Logback components are loggers, appenders and encoders/layouts. The components work together to provide developer full control on how messages are logged, formatted, and where they are reported.

Logger

Loggers are the components that do the heavy work in logging. They capture the logging data and output it to a destination using appenders. The loggers used in an application are typically organized into a hierarchy and a root logger resides at the top of the hierarchy. It is the LoggerContext that is responsible for creating loggers and arranging them in a hierarchy.

Loggers maintains a hierarchical naming rule. As an example, a logger named guru is the parent of the logger, named guru.springframework and the ancestor of the logger, named guru.springframework.blog.
Logger Hierarchy

Apart from logger inheritance, an important logback concept is level inheritance, also referred as effective level. You can assign levels to loggers. Logback supports the TRACE, DEBUG, INFO, WARN and ERROR levels, as shown in this figure.

Log Levels

As you can see in the figure above, TRACE is the lowest level and the level moves up through, DEBUG, INFO, WARN, till ERROR, the highest level. This means that if you set the logger level to WARN, then only the WARN and ERROR level log messages will be displayed and the rest will be ignored.

In addition to the above levels, there are two special levels:

  • ALL: Turns on all levels.
  • OFF: Turns off all levels.

If a logger is not assigned a level, then level inheritance comes into play. The logger will inherit the level from its nearest ancestor with an assigned level. If none of the application loggers in the hierarchy have assigned level, the level of the root logger will be inherited. The default level of the root logger is DEBUG.

Note: While developing in your local machine, it is common to set the log level to DEBUG. This will give you detailed log messages for your development use. When deployed to a production environment, it’s typical to set the log level to ERROR. This is to avoid filling your logs with excessive debug information. Also, while logging is very efficient, there is still a cost to system resources.

Appenders

Once you capture logging information through a logger, you need to send it to an output destination. The output destination is called an appender, and it’s attached to the logger. Log4J 2 provides appenders for console, files, remote socket servers, SMTP servers, many popular databases (such as MySQL, PostgreSQL, and Oracle), JMS, remote UNIX Syslog daemons, and more.

Layouts/Encoders

An appender uses a layout to format a log event. A layout, which is an implementation of the Layout interface of log4j-core, transforms a log event to a string. A layout can’t control when log events get written out, and therefore can’t group events into batches. To address the limitations of layouts, logback introduced encoders in version 0.9.19. Encoders, which are implementation of the Encoder interface, transforms an incoming log event into a byte array and writes out the resulting array onto the appropriate output stream. Encoders have total control over the format of the bytes written out. In addition, encoders can control whether (and when) those bytes get written out. I’ll discuss more about layouts and encoders in upcoming posts on logback configuration.

Using Logback

We will start with a simple application that uses Logback for logging. To start with, we need the logback dependencies in our project. Out of the box, both Spring Boot core and web projects include the logback classic dependencies. This figure shows the logback dependencies included in Spring Boot.

Logback Dependencies via Sprint Boot
As shown in the figure above, the latest SpringBoot 1.3.3REALESE version as of writing this post uses Logback classic 1.1.5.

If you want to use different Logback and SLF4J versions or if you are not using SpringBoot, define their dependencies in your Maven POM, like this.

. . .
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.21</version>
</dependency>
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.1.7</version>
</dependency>
. . .

In an application, you can retrieve a logger by calling the getLogger() method of the SLF4J LoggerFactory class. There are two overloaded getLogger() methods. One returns a Logger instance named according to the string value passed as parameter. The other returns a Logger instance named corresponding to the class passed as parameter. The recommended strategy is to use the latter one. This is because in a large application with thousands of log statements, you will find it easy to identify the origin of a log message as the log output bears the name of the generating logger. Once you retrieve a Logger, you can call the log methods on it, like this.

LogbackDemo.java

package guru.springframework.blog.logbackoverview;


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

public class LogbackDemo {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    public void performTask(){
        logger.info("This is an {} message.","info");
        logger.warn("This is a warn message.");
        logger.error("This is an error message.");
        logger.debug("This is a debug message.");
    }
}

In Line 8 of the LogbackDemo class above, we retrieved a Logger object with a call to the static Loggerfactory.getLogger() method. Passing LogbackDemo.class to getLogger() instead of this.getClass() will produce the same result. But I suggest passing this.getClass() to decouple the statement from a particular class, thereby making it reusable across other logging classes. From Line 10- Line 13 we called the log methods on the retrieved logger. Notice Line 10 that uses parameterized message in the info() method. You can use such parameterized log messages in the other logging methods too.

To test the preceding logger, we will use JUnit. The test class is this.

LogbackDemoTest.java

package guru.springframework.blog.logbackoverview;

import org.junit.Test;

import static org.junit.Assert.*;

public class LogbackDemoTest {

    @Test
    public void testPerformTask() throws Exception {
        LogbackDemo logBackDemo = new LogbackDemo();
        logBackDemo.performTask();
    }
}

When you run the test class, the log messages of LogbackDemo are sent to the console.
Log Output in Console

Summary

In the example of this post, you may have noticed that I did not specify any appender/encoder or layout for the logger. Rather, I relied upon defaults inherited from the logback root logger. By default, the root logger is associated with the console appender and have the DEBUG level, and our logger inherited both. Therefore, debug and higher log messages were sent to the IntelliJ console. However, in enterprise applications its likely you’ll work with external configuration files to use more advanced features of Logback. These configuration files can be XML or Groovy to specify Logback configuration options. In upcoming posts, I’ll discuss using both XML and Groovy external configuration files to help you explore what a powerful logging tool Logback is.

About jt

    You May Also Like

    8 comments on “Logback Introduction: An Enterprise Logging Framework

    1. May 12, 2016 at 1:35 am

      nice short intro for someone coming from log4j2 like myself. seems odd to call slf4j just SL4J though

      Reply
    2. July 5, 2017 at 3:31 am

      Thanku so much. It was very helpful. It helped to understand the basic as I was completely new to this concept
      .

      Reply
    3. August 28, 2018 at 9:38 am

      awesome explanation

      Reply
    4. January 31, 2019 at 8:37 am

      i had set up SMTPAppender .i will receiving mails successfully .but my requirement is to receive mails of ERROR related to only packages in spring boot.is this possible

      Reply

    Leave a Reply

    Your email address will not be published. Required fields are marked *

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