An Introduction to Logback: a Logging Framework From the Creator of Log4J
Monitoring, diagnosing, and troubleshooting are key activities in any enterprise application lifecycle, and logging is the core part of these activities. Meet Logback, a logging framework from the creator of Log4J.
Join the DZone community and get the full member experience.
Join For FreeLogback 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 logback-classic, which in going forward I’ll refer to as logback. Logback natively implements the Simple Logging Facade for Java (SL4J) API. In a nutshell, SL4J is a façade for various logging frameworks. As a developer, you’ll write logging code against the SL4J API. At deployment time, you have the flexibility to plug-in a desired logging framework, made possible through an intermediate SL4J bridge layer. As logback natively implements SL4J, the additional SL4J 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.
The key Logback components are loggers, appenders, and encoders/layouts. The components work together to provide the 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 maintain 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.
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.
As you can see in the figure above, TRACE is the lowest level and the level moves up through, DEBUG, INFO, WARN, until 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 a level, the level of the root logger will be inherited. The default level of the root logger is DEBUG.
Note: While developing on 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. Logback provides appenders for the 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 logback-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 implementations of the Encoder interface, transform an incoming log event into a byte array and write 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.
As shown in the figure above, the latest SpringBoot 1.3.3RELEASE version as of writing this post uses Logback classic 1.1.5.
If you want to use different Logback and SL4J 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 SL4J LoggerFactory class. There are two overloaded getLogger() methods. One returns a Logger instance named according to the string value passed as a parameter. The other returns a Logger instance named corresponding to the class passed as a 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.
Summary
In the example in 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 has the DEBUG level, and our logger inherited both. Therefore, debug and higher log messages were sent to the IntelliJ console. However, in enterprise applications it's 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.
Published at DZone with permission of John Thompson, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments