If you ever had to analyze an issue in production, I’m sure you know how important it is to have good logging. Good logging requires three things:

The log messages need to provide the required information to understand what the application does internally. Writing log messages has to be as efficient as possible so that it doesn’t affect the performance of your application. You need to be able to adapt the logging details to different deployment environments and situations.

While you still need to decide yourself which log messages you should write for each use case, you don’t need to worry about requirement 2 and 3. Various logging frameworks already solved these technical requirements. You only need to choose one of them and use it to write your log messages.

To make it even better, SLF4J provides a standardized API that in one way or the other is implemented by most of these frameworks. That enables you to change your logging framework without changing your code. You only need to change the dependency to a different framework that implements the SLF4J interfaces.

Tip: Find application errors and performance problems instantly with Stackify Retrace Troubleshooting and optimizing your code is easy with integrated errors, logs and code level performance insights.

Writing Log Messages with SLF4J

Writing log messages with SLF4J is very easy. You first need to call the getLogger method on the LoggerFactory to instantiate a new Logger object. You can then call one of the debug, info, warning, error or fatal methods on the Logger to write a log message with the corresponding log level. Here you can see a typical example:

public class MyClass { Logger log = LoggerFactory.getLogger(this.getClass().getName()); public void myMethod() { log.info("This is an info message"); // ... } }

So if these frameworks are easily interchangeable, which one should you choose?

The answer to this question is not as easy as you might expect. There are several frameworks available that are broadly used in the Java world. In this article, I want to introduce you to Log4j and its two successors Logback and Log4j2.

Apache Log4j

Apache Log4j is a very old logging framework and was the most popular one for several years. It introduced basic concepts, like hierarchical log levels and loggers, that are still used by modern logging frameworks.

The development team announced Log4j’s end of life in 2015. While quite a few legacy projects still use it, you should prefer one of the other frameworks discussed in this article if you start a new project.

Matt already explained Log4j in great detail in a previous article, and you can use the SLF4JAPI that I showed you earlier to write log messages with Log4j. So let’s take a quick look at the required dependencies and configuration before we talk about Logback and Log4j2.

Required dependencies

If you want to use Log4j in your application, you need to add the log4j.jar file to your classpath. You can see the required Maven dependency in the following code snippet.

<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>

Log4j doesn’t support SLF4J natively. You also need to add the following dependency to be able to use Log4j via the standardized interfaces.

<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <scope>test</scope> </dependency>

Configuring Log4j

In addition to the log4j.jar file, you need to define your appender and logger with their log levels in the log4j.properties file. The appender writes the log messages to a destination such as a file or database. The logger and level define the granularity of log messages that are written to the log file.

The following code snippet shows a typical Log4j configuration for a development system of an application that uses Hibernate as an object-relational mapper. It writes all log message to the file app.log and sets the general log level to INFO. The configuration also sets the log levels of the logger org.hibernate.SQL to DEBUG and the category org.hibernate.type.descriptor.sql to TRACE. These are 2 of Hibernate’s loggers that write the executed SQL statements and their bind parameter values to the configured file appender.

log4j.appender.file=org.apache.log4j.FileAppender log4j.appender.file.File=app.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] - %m%n log4j.rootLogger=info, file # basic log level for all messages log4j.logger.org.hibernate=info # SQL statements and parameters log4j.logger.org.hibernate.SQL=debug log4j.logger.org.hibernate.type.descriptor.sql=trace

Based on this configuration, you can write your log messages using the SLF4J API. That’s all about Log4j for now. If you want to learn more about it, please take a look at Matt Watson’s Ultimate Log4j Tutorial.

Logback

Logback was written by the same developer who implemented Log4j with the goal to become its successor. It follows the same concepts as Log4j but was rewritten to improve the performance, to support SLF4J natively, and to implement several other improvements like advanced filtering options and automatic reloading of logging configurations.

The framework consists of 3 parts:

logback-core logback-classic logback-access

Logback-core provides the core functionality of the logging framework. Logback-classic adds more features to the core functionality, e.g., native support for SLF4J. And logback-access integrates it with servlet containers so that you can use it to write HTTP-access logs.

Required dependencies

You only need to define a dependency on logback-classic. It transitively includes the dependencies to logback-core and the SLF4J API.

<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>

Configuring Logback

Logback doesn’t require any configuration. By default, it writes all log messages in DEBUG level or higher to standard out. You can change that with a custom configuration file in XML or Groovy format.

Logback uses the same concepts as Log4j. So it’s no surprise that even if they are using different file formats, their configurations are very similar. The following code snippet shows the same configuration as I used with Log4j.

<configuration> <appender name="FILE" class="ch.qos.logback.core.FileAppender"> <file>app.log</file> <encoder> <pattern>%d{HH:mm:ss,SSS} %-5p [%c] - %m%n</pattern> </encoder> </appender> <logger name="org.hibernate.SQL" level="DEBUG" /> <logger name="org.hibernate.type.descriptor.sql" level="TRACE" /> <root level="info"> <appender-ref ref="FILE" /> </root> </configuration>

After you’ve added the required dependency and configured Logback, you can use it to write log messages via the SLF4J API. So if you want to benefit from the improvements provided by Logback, you don’t need to change any code to replace Log4j with Logback.

Let’s now take a look at Log4j2. If you want to learn more about Logback, please take a look at Eugen’s in-depth article about it.

Apache Log4j2

Apache Log4j2 is the youngest of these three frameworks, and its goal is to improve on both of them by providing its own improvements on Log4j, including some of the improvements included in Logback and avoiding problems of Log4j and Logback.

So like Logback, Log4j2 provides support for SLF4J, automatically reloads your logging configuration, and supports advanced filtering options. In addition to these features, it also allows lazy evaluation of log statements based on lambda expressions, offers asynchronous loggers for low-latency systems, and provides a garbage-free mode to avoid any latency caused by garbage collector operations.

All these features make Log4j2 the most advanced and the fastest of these three logging frameworks.

Required Dependencies

Log4j2 packages its API and implementation in two separate jar files. You can implement and build your application using the log4j-api.jar, and you need to provide the additional log4j-core.jar at runtime. If you want to use the SLF4JAPI, you also need the log4j-slf4j-impl.jar file, which contains a bridge between the two APIs.

<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.11.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.11.1</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.11.1</version> </dependency>

Configuring Log4j2

The configuration of Log4j2 follows the same principles as the configuration of the two previous logging frameworks and, therefore, looks pretty similar.

<Configuration status="info"> <Appenders> <File name="FILE" fileName="app.log"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </File> </Appenders> <Loggers> <Logger name="org.hibernate.SQL" level="DEBUG"> <AppenderRef ref="FILE"/> </Logger> <Logger name="org.hibernate.type.descriptor.sql" level="TRACE"> <AppenderRef ref="FILE"/> </Logger> <Root level="info"> <AppenderRef ref="FILE"/> </Root> </Loggers> </Configuration>

Conclusion

Log4j, Logback, and Log4j2 are good logging frameworks that are broadly used. So which one should you use?

I recommend using Log4j2 because it’s the fastest and most advanced of the three frameworks. Logback is still a good option, if performance is not your highest priority.

Stackify’s Application Performance Management tool, Retrace offers log management for your Java applications. Try Retrace’s free, 14 day trial today.