7 minutes read

Logging means keeping records of various events and messages in a computer program. Why do we keep logs? We often need specific information about our program, such as user interactions, network status, errors, warnings, debugging information, stack traces in case of exceptions, and so on. It helps us analyze how the program runs and what events and errors occur at runtime: knowing why and when certain errors are thrown, we can fix them.

There are special tools that can help us set up effective and efficient logging, but before we discuss the logging libraries available for Java, let's see what we need such libraries to do.

What logging tools can do

The first thing you need to know is that we cannot rely on printing log messages to println() or print(). Our app may run on a remote server or a consumer device, or it may be inaccessible to us for some other reasons, and if we still want to see logs, they must be written to a file or files so we can check them later.

Here are some other things we should take into account when choosing a logging tool:

1. We should be able to control from which components of our app we want to receive log messages.
2. We should be able to filter out logging messages depending on their severity level.
3. Logs should be properly formatted for human or machine parsing.
4. Recording logs asynchronously could help us avoid interrupting the main functionality of the app even if it is heavily loaded.

Now, let's take a look at some available logging libraries and their functionalities and compare their advantages and disadvantages.

Image with the name of the logging libraries

Log4j

The first version of Log4j was released in 1999. It quickly became very popular and remained so for a long time. Now it has been replaced by its successor, Log4j 2. It is a high-performance modern logging library with many advanced features: apart from strings, it supports logging messages, lambda expressions, filtering based on context, markers, regular expressions, and other components of log events. Also, it allows users to create custom log levels.

Log4j 2's API is separate from the implementation, which allows for using it as a facade for other logging libraries, such as Log4j version 1.2, SLF4J, Apache Commons Logging, and java.util.logging.

Dependencies for your project as well as technical documentation can be found on the official site.

"Facade" here means that a library provides a simple but powerful interface to other logging libraries and frameworks, which allows us to easily use any logger hidden behind the facade's (for example, Log4j 2's) interface and not to worry about implementation.

JUL

java.util.logging.Logger, which is commonly referred to as JUL, was introduced in Java 1.4. Its most important benefit is that it is part of the Java standard library, which means that it's always at hand and you don't need to add another dependency to your project. However, JUL is not very performant and is less flexible than other popular logging libraries. Nevertheless, it's still a solid choice if you need logging but don't want to bother with third-party solutions. Also, the functionality of JUL can be enhanced by using such facades as SLF4J.

SLF4J

The simple logging facade for Java, or SLF4J, serves as an abstraction or facade for various logging tools. SLF4J consists of the common part (API) and a few libraries, which implement individual plug-in schemes: SLF4J -> Log4j, SLF4J -> java.util.logging, and so on. These libraries can be plugged in by simply putting them in a classpath. These features make SLF4J a log management tool. It is very popular and is used in big projects, such as Jetty and Hibernate.

The simple logging facade for Java, or SLF4J

Logback

The Logback project was created as a successor to Log4j. It is an up-to-date and performant logging library that natively implements SLF4J's API to allow switching between Logback and other logging frameworks. Also, it integrates with Servlet containers, such as Tomcat and Jetty, and provides the HTTP-access logging functionality.

Logback was created by the same developer that worked on Log4j. It is based on the same concepts and provides a number of improvements, including (but not limited to) advanced filtering options and automatic reloading of logging configurations. This library is a close competitor to Log4j 2 and is used as a logger on modern high-performance servers.

Kotlin-logging

Kotlin-logging is a registration library whose facade is SLF4J. If you wonder why we need yet another similar library, it is worth noting that kotlin-logging is written in 100% Kotlin. It is a lightweight logging structure for Kotlin: check out the GitHub page for a detailed reference material. Essentially, it allows you to create easily reproducible statements that are easy to identify and use to call log methods.

In order to add kotlin-logging to our project, we can use Maven:

<dependency>
    <groupId>io.github.microutils</groupId>
    <artifactId>kotlin-logging-jvm</artifactId>
    <version>3.0.4</version>
</dependency>

Alternatively, we can use Gradle:

implementation ("io.github.microutils:kotlin-logging-jvm:3.0.4")

The current version (relevant for January 2023) is 3.0.4. You can check out the latest version on the GitHub page. Since kotlin-logging is a wrapper for SLF4J, which itself is a wrapper for different logging backends, we’re going to need a configuration. In our case, we’ll be using Logback, which we’ll add using Maven:

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

We can also use Gradle:

implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.4.5'

And now, look at a small example:

import mu.KotlinLogging

private val kLogger = KotlinLogging.logger {}

fun main() {
    kLogger.trace { "This is the trace log level" }
    kLogger.debug { "This is the debug log level" }
    kLogger.info { "This is the info log level" }
    kLogger.info("Another info log message")
    kLogger.warn { "This is the warn log level" }
    kLogger.error { "This is the error log level" }
}

As you can see, to use kotlin-logging, we just need to declare an instance of the KotlinLogging class private val kLogger = KotlinLogging.logger {}. The output to the console will be as follows:

Example of output of program logs to the console

We've reviewed the use of kotlin-logging in the JVM. It is worth noting that this is just one of the implementations, as kotlin-logging has multi-platform support. Starting with version 2.0, the framework includes the following modules:

  • Common – artifact id: kotlin-logging

  • JVM – artifact id: kotlin-logging-jvm

  • JS – artifact id: kotlin-logging-js

  • Linux – artifact id: kotlin-logging-linuxx64

Check out the GitHub reference to find out how to use kotlin-logging on other platforms.

Conclusion

You may think that there are too many logging tools out there, and you are absolutely right! The great diversity of logging tools in the Java ecosystem is a result of the long evolution of multiple open-source projects adopted by the community. You can choose a logger library from quite a few available solutions depending on your needs and preferences. Many popular frameworks are shipped with built-in logging libraries, some of which we discussed in this topic. You can even use multiple logging libraries in a single project, as some of them can work as facades for others and ensure good interoperability.

There is no single best logging tool for Java, and the choice will always depend on many factors, so don't be afraid to try different tools and find out which one works for you.

14 learners liked this piece of theory. 3 didn't like it. What about you?
Report a typo