How Java logging should work


In this article, we will explore the following topics:

  • how java logging works
  • what is purpose of slf4j
  • what is the purpose of commons-logging
  • what is the difference between log4j and slf4j
  • what is the difference between logback and slf4j
  • what is the difference between commons-logging and log4j
  • what is the difference between commons-logging and logback
  • how to redirect jul to slf4j
  • how to redirect commons-logging to slf4j

Let’s start

To begin with, there are two main concepts in Java logging:

  • logging abstraction library (logging facade). Examples: slf4j, commons-logging
  • logging implementation library. Examples: log4j, log4j2, logback

The problem

Suppose that you’re Java library developer (let’s say it’s called our_library), not standard Java application developer.

You need to have logging in your library. Suppose you have chosen log4j.

Then there is consumer of our library (another developer). He depends on our_library and another one (another_library).

Suppose that another_library is using logback. So the developer, who uses both another_library and our_library will be losing time trying to understand how to combine both log4j and logback in one application.


Solution

The solution to that problem is logging facade. Logging facade is the thing, that can redirect log events to some number of logging implementation libraries. our_library and another_library should use a logging abstraction library, instead of a logging implementation library.

Thus, our poor developer can make the choice of logging implementation library itself. Whichever library he chooses, logging facade in our_library and another_library are going to send logging event to it.

Java libraries should use logging facade such as slf4j. It gives the logging-library-independence to all developers, who going to use such java libraries.

Applications will be having slf4j as a transitive dependency, and ability to choose logging library implementation themselves. It may be log4j2 or logback.


Equalizing logging facades

You may ask – what if some library using another logging facade, not slf4j? Apache’s commons-logging, for example. There is certain technology called bridge.

Here is the answer to the question: how to redirect commons-logging to slf4j.

If some library is using Apache’s commons-logging, we can redirect it to the slf4j by using bridge. Add this to your pom.xml:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>/*...*/</version>
</dependency>

And exclude Apache’s commons-logging classes from your jar. It can be done by marking dependency as provided:

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>[1.0,)</version> <!-- excluding all versions -->
    <scope>provided</scope>
</dependency>

If you using Spring, that you also need to add exclusion to spring-boot-maven-plugin, because it going to create another jar (after Maven’s jar):

 1<plugin>
 2    ...
 3    <executions>
 4        <execution>
 5            ...
 6            <configuration>
 7                ...
 8                <excludes>
 9                    <exclude>
10                        <groupId>commons-logging</groupId>
11                        <artifactId>commons-logging</artifactId>
12                    </exclude>
13                </excludes>
14            </configuration>
15        </execution>
16    </executions>
17</plugin>

How do such bridges work? slf4j bridge, which redirects log events from commons-logging to slf4j merely is jar, which contain same-name-classes as presented in commons-logging. Such classes, which are replacement for originals, perform redirection work.

That is the point because of which we need to exclude original commons-logging classes.


Redirecting jul to slf4j

There is a dinosaur called java.util.logging, which some library may use instead of logging facade. We may want to redirect to slf4j.

We can also use bridge technology to perform that. Add that dependency to your pom.xml:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    <version>...</version>
</dependency>