By Shangzuo
After reading through the previous article of the series, which is a little bit theoretical, we need to do something practical now. In this article, I will introduce the optimal logging solution — SLF4J + Logback — that I have discovered in practice. The reasons why I choose this solution are as follows:
Next, let's set up the environment based on the theoretical knowledge explained above. I will give you the general idea first, and then the overall dependency configuration method. In order to save storage space, dependencies will be expressed in GAV coordinates separated by a colon(:), instead of XML.
We can easily learn from the above-mentioned knowledge that the following three packages are necessary:
org.slf4j:slf4j-api
.ch.qos.logback:logback-core
.ch.qos.logback:logback-classic
.The logback-classic package directly depends on the other two packages, and they are definitely of the most suitable versions for logback-classic. Therefore, your project can explicitly depend on only the logback-classic package to prevent ambiguity. If you want to increase the weight of a version, your project can depend only on this version.
In addition, it should be noted that the versions of SLF4J and Logback are not completely forward-compatible. The correspondence between SLF4J and Logback versions will be described below.
A great update in SLF4J 2.0.x[3] is that Java Development Kit (JDK) ServiceLoader[4], a service provider interface (SPI), is used to automatically load and implement the org.slf4j.impl.StaticLoggerBinder
class, without the need to search for the class. This is a feature supported in JDK 8, demonstrating SLF4J's dependency on JDK.
SLF4J Version | JDK Version | Remarks |
SLF4J 1.7.x | JDK 1.5 or later | |
SLF4J 2.0.x | JDK 8 or later | |
SLF4J 2.1.x | Probably JDK 11 or a later version | Ceki is asking for comments.[5] |
After releasing a few alpha/beta versions on the SLF4J 1.8.x phase, the developer of SLF4J directly released the 2.0.x version. Therefore, SLF4J 1.8.x is not discussed here.
Due to changes to the SLF4J technology, matching logback-classic versions must be used to implement the SLF4J API. Otherwise, issues will occur. (For more information, see chapter 8 "FAQ".)
Logback Version | SLF4J Version | JDK Version | Remarks |
Logback 1.2.x | SLF4J 1.7.x | JDK 1.5 or later | |
Logback 1.3.x | SLF4J 2.0.x | JDK 8 or later | |
Logback 1.4.x | SLF4J 2.0.x | JDK 11 or later | |
Logback 1.5.x | SLF4J 2.0.12 or later | JDK 11 or later | 1.5.x was released to replace 1.4.x. |
Logback 1.3.x and 1.4.x listed in the preceding table are concurrently maintained. You need to choose the matching version (1.3.n
or 1.4.n
) based on the JDK version of your project. However, Logback has been fully updated to the 1.5.x series, and series 1.3.x and 1.4.x are no longer maintained[6]. For more information, see the version release document[7] on the official Logback website.
The version compatibility tables in sections 2.1 and 2.2 indicate that:
However, the logging system used by Spring Boot applications[8] has additional requirements for SLF4J and Logback versions. We'll discuss this in the next chapter.
Spring Boot provides the spring-boot-starter-logging[9] dependency for projects to directly use Logback (and indirectly use SLF4J) for logging. After the dependency is added, the Spring Boot logging system uses org.springframework.boot.logging.LoggingSystem[10]
to search for logs and automatically adapt them. This way, we do not need to worry about logging dependency issues in most cases when using Spring Boot. However, some Spring Boot versions may not support this dependency due to inconsistent implementations between SLF4J 2.0.x and SLF4J 1.7.x.
Spring Boot Version | SLF4J Version | Logback Version | JDK Version |
Spring Boot 1.5 | SLF4J 1.7.x | Logback 1.1.x | JDK 7 or later |
Spring Boot 2.x | SLF4J 1.7.x | Logback 1.2.x | JDK 8 or later |
Spring Boot 3.x | SLF4J 2.0.x | Logback 1.4.x | JDK 17 or later |
Based on the preceding table and the version compatibility correspondences summarized in chapter 2, the following conclusions can be drawn:
If you use an earlier version of Spring Boot and want to use the latest SLF4J or Logback, you can refer to a related article posted on GitHub[11], where many developers comment their adaptation solutions[12]. However, I haven't verified these solutions yet. So, hope you find one that works.
Adapters can leverage bridging to ensure proper log printing when the second- and third-party packages that your project depends on use JCL, Log4j, Log4j2, or JUL for logging.
org.slf4j:jcl-over-slf4j
: Bridges JCL to SLF4J.org.slf4j:log4j-over-slf4
j: Bridges Log4j to SLF4J.org.slf4j:jul-to-slf4j
: Bridges JUL to SLF4J.org.apache.logging.log4j:log4j-to-slf4j
: Bridges Log4j 2 to SLF4J.Note that the versions of all org.slf4j
packages must be identical, which means that the versions of introduced bridge packages must match the selected slf4j-api version. Therefore, a Bill of Materials (BOM) file has been added to projects since SLF4J 2.0.8. This way, you do not need to separately maintain every version package. (For versions earlier than 2.0.8, you still need to ensure the version consistency on your own efforts.)
<dependencyManagement>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-bom</artifactId>
<version>2.0.9</version>
<type>pom</type>
</dependency>
</dependencyManagement>
To my surprise, the log4j-to-slf4j
package supports both SLF4J 1 and SLF4J 2.
In fact, a bridge secretly forwards a call to another API by using the same package structure as the destination package. Therefore, if the bridge and the destination package are introduced at the same time, a package collision may occur.
As many tools inadvertently introduce logging facades or implementations, it is necessary to check the entire application and exclude useless facades or implementations, including JCL, Log4j, and Log4j 2.
commons-logging:commons-logging
log4j:log4j
org.apache.logging.log4j:log4j-core
What's more, other bridge packages that are indirectly introduced in your project may also cause package collisions and need to be excluded. You can determine whether a package is needed by referring to the package relationship diagram in chapter 3 "Summary" in the first article of the this series. The real project environment is complex. So, I will not enumerate these packages here.
Grade provides all*.exclude[13]
to enable global package exclusion.
Unfortunately, Maven has not provided the global package exclusion capability yet. (This issue[14] was proposed as early as in 2006, but the required capability is still not supported now.) It is not realistic to perform exclusion each time when a useless package is introduced. Based on my past experience, I figure out the following answers to the "Is there a way to exclude a Maven dependency globally?" question raised on Stack Overflow[15]:
999-not-exist
) to exclude the specified package. However, such an empty package usually do not exist in the Maven central repository, but it may be found in private repositories. You can build your own private repository and upload it, or directly use Version 99 Does Not Exist[16]. This is the optimal solution which applies to both local and remote environments.<scope>provided</scope>
to label the package to be excluded. In this case, the package will be skipped during compilation. However, the package will still be introduced in the local environment, resulting in inconsistency with the remote environment and thus hindering the debugging.After you perform the preceding steps, the dependencies are finalized. The final dependencies are of the latest versions that meet the requirements as of the date I finished this article (April 2024).
Basic packages
org.slf4j:slf4j-api:1.7.36
ch.qos.logback:logback-core:1.2.13
ch.qos.logback:logback-classic:1.2.13
Bridge packages
org.slf4j:jcl-over-slf4j:1.7.36
org.slf4j:log4j-over-slf4j:1.7.36
org.slf4j:jul-to-slf4j:1.7.36
org.apache.logging.log4j:log4j-to-slf4j:2.23.1
Exclusion packages
commons-logging:commons-logging:99.0-does-not-exist
log4j:log4j:99.0-does-not-exist
org.apache.logging.log4j:log4j-core:99.0-does-not-exist
Basic packages
org.slf4j:slf4j-bom:2.0.12
(The BOM file package is used to collectively manage dependencies.)ch.qos.logback:logback-core:1.4.14
ch.qos.logback:logback-classic:1.4.14
Bridge packages
org.slf4j:jcl-over-slf4j
(For more information, see chapter 7 "Usage notes".)org.slf4j:log4j-over-slf4j
(For more information, see chapter 7 "Usage notes".)org.slf4j:jul-to-slf4j
(For more information, see chapter 7 "Usage notes".)org.apache.logging.log4j:log4j-to-slf4j:2.23.1
Exclusion packages
commons-logging:commons-logging:99.0-does-not-exist
log4j:log4j:99.0-does-not-exist
org.apache.logging.log4j:log4j-core:99.0-does-not-exist
As what we said earlier, you need to perform the following two steps in project building:
The preceding two steps are usually performed in the <dependencyManagement>
section of a parent POM, but this section is available only in the management package version and will not be loaded until the project is actually referenced.
In practice, we often use the following approach for project building:
log4j:log4j
package to print logs.log4j:log4j
package from a parent POM, an error will occur when module A uses Log4j.log4j-over-slf4j
in the parent POM to switch from Log4j to SLF4J.This approach seems perfect, and the project can start as expected by following it. However, when module A needs to print logs, the error message "log4j:WARN No appenders could be found for logger (xxx.xxx.xxx)
" is displayed, which means that log4j-over-slf4j
is not really introduced in our project. (Nearly no second-party dependency allows such a package to be introduced unless it wants to be denounced.)
Actually, the solution is simple. That is, introduce log4j-over-slf4j
through the <dependencies>
section either of a parent POM or a child POM with actual dependencies.
Normally, if you strictly follow the above-mentioned solutions and procedure, you may not encounter the following issues. However, difficulties may hit upon you in project maintenance accidentally.To help you find the causes and address these difficulties, I collect the errors that are frequently reported during logging.
When I wrote this chapter, I referred to a lot of useful information on the official websites of SLF4J and Logback, including:
Error 1: LoggerFactory is not a Logback LoggerContext but Logback is on the classpath. Either remove Logback or the competing implementation
A package collision occurs. You can directly exclude the unnecessary SLF4J adapter. The collision often happens between logback-classic
and slf4j-log4j12
. In this case, you just need to exclude the one that is not needed based on the logging framework (Logback or Log4j 2) in use.
To be specific, the reason is that Spring Boot obtains the available valid logging system by using LoggingSystem
and supports SLF4J, Logback, Log4j 2, and JUL by default when it starts. (For more information, see chapter 3 "Adaption to Spring Boot".)
Implementation of LoggingSystem
Adapters of Logback and SLF4J (such as slf4j-log4j12
) share the StaticLoggerBinder
implementation. Therefore, this error is reported if the hit is not a Logback implementation when Spring Boot uses Logback.
Error 2: java.lang.NoClassDefFoundError: org/slf4j/impl/StaticLoggerBinder
The versions do not match each other. SLF4J has been loaded in SPI mode since the 2.0.x series (for more information, see section 2.1 "SLF4J version compatibility"). Therefore, when the versions of SLF4J and Logback (or Log4j 2) you introduced do not match each other, this error is reported.
Error3: java.lang.ClassNotFoundException: org.slf4j.impl.StaticLoggerBinder
Same answer as that to Error 2.
Error 4: java.lang.ClassCastException:org.apache.logging.slf4j.SLF4JLoggerContext cannot be cast to org.apache.logging.log4j.core.LoggerContext
A package collision occurs. It is highly probable that both Log4j 2 and a bridge that targets Log4j 2 are introduced. For the detailed reason, see chapter 5 "Exclude useless dependencies". However, I cannot help you determine the specific package exclusion solution because it depends on which logging system you want to use. Remember that you can retain only one package of the collided two.
Error 5: java.lang.ClassCastException:org.slf4j.impl.Log4jLoggerFactory cannot be cast to ch.qos.logback.classic.LoggerContext
A package collision occurs. It is highly probable that multiple SLF4J adapters (such as logback-classic
and slf4j-log4j12
) are introduced at the same time. The cause and solution to this issue are similar to those to Error 4.
Error 6: SLF4J: No SLF4J providers were found.
Only slf4j-api
exists, and no adapter is available. This issue can be easily solved by adding logback-classic
or slf4j-log4j12
.
Error 7: SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
Only slf4j-api
exists, and no adapter is available. This issue can be easily solved by adding logback-classic
or slf4j-log4j12
. However, I performed a test and found that the error was reported occasionally even though logback-classic
existed, the project started properly, and the log output was normal. I haven't figured out the reason yet, and hope you guys could find me the answer.
Error 8: SLF4J: Class path contains multiple SLF4J bindings.
Multiple SLF4J adapters are found. Generally, the paths of all adapter packages are listed at the end of this error log. You only need to find the unnecessary packages and exclude them. SLF4J selects the first adapter by default. Therefore, if only this error is reported, the system may still properly start, print logs, and perform normally.
Error 9: log4j:WARN No appenders could be found for logger (xxx.xxx.xxx)
In most cases, this issue can be solved by introducing log4j-over-slf4j
in your project. For specific reasons, see chapter 7 "Usage notes".
Error 10: java.lang.UnsupportedClassVersionError:ch/qos/logback/classic/spi/LogbackServiceProvider has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
You are using JDK 8 (52.0), but the introduced Logback version is 1.3 or later, which only supports JDK 11 (55.0) or later. For more information, see section 2.2 "Logback version compatibility."
Error 11: Failed to load class org.slf4j.impl.StaticLoggerBinder
I have encountered this error during my project building, but everything is normal in my project and I haven't found the reason yet.
[1] https://juejin.cn/post/6915015034565951501
[2] https://logging.apache.org/log4j/2.x/manual/extending.html
[3] https://www.slf4j.org/faq.html#changesInVersion200
[4] https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html
[5] https://github.com/qos-ch/slf4j/discussions/379
[6] https://logback.qos.ch/download.html
[7] https://logback.qos.ch/news.html
[8] https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.logging
[9] https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-logging
[10] https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/logging/LoggingSystem.html
[11] https://github.com/spring-projects/spring-boot/issues/12649
[12] https://github.com/spring-projects/spring-boot/issues/12649#issuecomment-1569448932
[13] https://stackoverflow.com/questions/55441430/what-does-this-all-exclude-means-in-gradle-transitive-dependency
[14] https://issues.apache.org/jira/browse/MNG-1977
[15] https://stackoverflow.com/questions/4716310/is-there-a-way-to-exclude-a-maven-dependency-globally
[16] https://github.com/erikvanoosten/version99
[17] https://maven.apache.org/enforcer/maven-enforcer-plugin/
[18] https://www.slf4j.org/codes.html
[19] https://www.slf4j.org/faq.html
[20] https://www.slf4j.org/legacy.html
[21] https://logback.qos.ch/codes.html
[22] https://logback.qos.ch/faq.html
Disclaimer: The views expressed herein are for reference only and don't necessarily represent the official views of Alibaba Cloud.
Interview Questions We've Learned Over the Years: Algorithm Practices
Alibaba’s Immersive Paris Showcase Explores AI-backed Future of Retail
1,076 posts | 265 followers
FollowAlibaba Cloud Community - August 13, 2024
Alibaba Cloud Community - September 4, 2024
Alibaba Cloud Community - June 14, 2024
Alibaba Cloud Community - November 9, 2021
Alibaba Cloud Community - August 5, 2024
Alibaba Cloud Community - August 5, 2024
1,076 posts | 265 followers
FollowExplore Web Hosting solutions that can power your personal website or empower your online business.
Learn MoreA low-code development platform to make work easier
Learn MoreExplore how our Web Hosting solutions help small and medium sized companies power their websites and online businesses.
Learn MoreHelp enterprises build high-quality, stable mobile apps
Learn MoreMore Posts by Alibaba Cloud Community