preface
Have you ever had a log configured, but it didn’t print?
Have you ever encountered a log4J error on startup after configuring LogBack? Something like this:
log4j:WARN No appenders could be found for logger (org.example.App). log4j:WARN Please initialize the log4j system Properly. Log4j: WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.Copy the code
Have you ever encountered this error with SLF4J?
SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [the jar file: / C: / Users/jiang/m2 / repository/ch/qos/logback/logback - classic / 1.2.3 / logback - classic - 1.2.3. Jar! / org/slf4j impl / StaticLoggerBinder.class] SLF4J: Found binding in [the jar file: / C: / Users/jiang/m2 / repository/org/slf4j/slf4j - log4j12 1.7.30 / slf4j - log4j12-1.7.30. Jar! / org/slf4j/impl/StaticL oggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]Copy the code
Have you ever had a DUBBO log print problem?
Have you ever encountered Mybatis SQL log print out the situation?
Have you ever encountered a JPA/Hibernate SQL log that cannot be printed?
Have you ever been on a complex project where many of the internal framework logs failed to print?
Have you ever encountered a Tomcat project where log files were printed in multiple copies, Catalina.out and other files?
Have you ever had a problem with the SpringBoot project printing multiple log files?
Have you encountered any log configuration issues…
Conflicts with logging frameworks
These problems are basically caused by the coexistence or incorrect configuration of multiple logging frameworks. So why coexistence or conflict?
Generally, there are the following reasons:
- Project manual refers to the various logging framework package – such as reference at the same time the log4j/log4j2 / logback/jboss logging/JCL, etc
- Dubbo Dependencies on zkClient (zkClient) and log4j (ZkClient). So there’s going to be coexistence of multiple sets
- Multiple versions of the same logging framework coexist
Various logging frameworks in JAVA
Before we get into conflict and resolution, let’s briefly talk about the various logging frameworks in Java:
Logging frameworks in Java are divided into two types, namely logging abstraction/facade and logging implementation
Log abstraction/facade
Log abstraction/facade, they are not responsible for specific log printing, such as output to files, configuring log content formatting, etc. They are simply a set of log abstractions that define a uniform set of log printing standards, such as Logger objects, Level objects.
Slf4j (Simple Logging Facade for Java) and JCL (Apache Commons Logging) are the most popular Logging abstractions in Java. There is also jboss-Logging, which is used for the JBoss family of software such as Hibernate. Like JCL, which has not been updated for years (the last update was 14 years ago), slF4J is currently the most recommended
The logging implementation
Java log implementation framework, the mainstream has the following:
- Log4j-apache log4j-apache log4j2
- Log4j2 – Apache (new version of log4j, currently the most asynchronous I/O performance, easy to configure)
- Logback-qos (SLF4J is the product of this company)
- Jul (java.util.logging) – built-in JDK
In the program, you can use the logging framework directly, or you can use logging abstraction + logging to implement a matching solution. However, it is generally implemented with log abstraction + log, which is more flexible and easier to adapt.
The most popular solution is SLF4J + Logback /log4j2. However, jBoss-Logging is more likely to be used for jBoss products. Jboss-logging is built into frameworks like JPA/Hibernate
Examples of SpringBoot + Dubbo logging framework conflicts
For example, one of the most common types of coexistence conflicts caused by transitive dependencies:
For example, if I have a “clean” spring-boot project, so clean that there is only one spring-boot-starter dependency, and I want to integrate Dubbo and use ZooKeeper as the registry, my dependency configuration looks like this:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.9</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>2.7.9</version>
</dependency>
</dependencies>
Copy the code
Now start the spring-boot project and see a bunch of red errors:
SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [the jar file: / C: / Users/jiang/m2 / repository/ch/qos/logback/logback - classic / 1.2.3 / logback - classic - 1.2.3. Jar! / org/slf4j impl / StaticLoggerBinder.class] SLF4J: Found binding in [the jar file: / C: / Users/jiang/m2 / repository/org/slf4j/slf4j - log4j12 1.7.30 / slf4j - log4j12-1.7.30. Jar! / org/slf4j/impl/StaticL oggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- human flesh line -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - the log4j: WARN No appenders could be found for logger (org.apache.dubbo.common.logger.LoggerFactory). log4j:WARN Please initialize the log4j system properly. Log4j: WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.Copy the code
From the error message, the error content is divided into two parts:
- Slf4j reported an error indicating that multiple SLF4J log bindings were found
- Log4j reported an error indicating that log4J has no appender configuration
This error occurs because dubbo’s pass-through dependency contains log4j, but the default configuration for Spring-Boot is SLf4J +logback. After relying on dubbo related packages, both logback/ JCL (Apache Commons-logging)/log4j/jul-to-slf4j/ slf4J-log4j/log4J-to-slf4j are now present in the project
Take a look at the dependency graph:
Slf4j-log4j is a slf4J implementation of log4j that uses log4j output when calling slf4J apis. Instead, log4J-to-slf4j replaces the log4J implementation with log4j, so it’s not an endless loop
But also the existence of logback logback default implementation the slf4j abstraction, and so do slf4j – log4j realized slf4j abstraction, logback, project in the realization of the coexistence of the two sets of slf4j, so at the time of use slf4j interface print will use which implementation?
The answer is “first”, the first Slf4j implementation class to be loaded, but this log configuration order, which depends on the ClassLoader loading order, is quite unreliable
In order for logging to work properly, and for all the frameworks in the project to print logs properly, the logging framework must be unified. However, the unity here is not to forcibly modify, but with the “adaptation/transfer” way.
Although slf4J-log4j is configured in the project, this configuration is suitable for log4j2, while our dependency is only log4j1. In fact, this transfer is invalid. But logback is valid and is the default for spring-Boot projects, so choose Logback as the unified logging framework for your projects.
A log4j(1) package exists in the project and a log4j(1) error is reported during startup, indicating that some code calls the LOG4j API. But we don’t want to use log4j, so we need to solve the log4j problem first.
Because there are references to the Log4j code, it must not be feasible to delete log4j directly. Slf4j provides a log4J-over-SLf4J package that copies the log4j1 interface class (Logger, etc.) and changes the implementation class to SLf4J.
So removing log4j’s (pass-through) dependencies and referencing log4J-over-slf4j solves this log4j problem. To check the Dependencies in the POM, use maven’s command, the Maven Dependencies Diagram of IDEA, or a plugin like Maven Helper.
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>2.7.9</version>
<scope>compile</scope>
<! - eliminate log4j - >
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
</exclusions>
</dependency>
<! Log4j-slf4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.30</version>
</dependency>
Copy the code
Having solved the log4j problem, there is now the problem of slF4J having two implementations, which is much easier to deal with. Since we plan to use logback, we just need to exclude/remove the slf4J-log4j implementation dependencies
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>2.7.9</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
Copy the code
After the modification is complete, restart again and there is no error, easily solve the problem
Log Adaptation
The above is just one way to convert, but there are so many logging frameworks that they can convert to each other. But the end result is bothUnify a set of logging framework, so that the final log implementation only one set** With so many log adaptation/transformation methods, it must be a bit difficult to remember them all. I drew a picture of thatIt may be the most complete log frame adaptation map on the whole network (the original picture is large, please click to enlarge it)In case of conflict and need to switch from one logging framework to another, you only need to import the relevant dependency packages according to the path shown in the figure. 六四屠杀For example, you want to adapt/convert SLF4J to log4j2. Following the path on the diagram, only references are requiredlog4j-slf4j-implCan.
To adapt/convert JCL to SLF4J, simply remove the JCL package and reference jCL-over-SLf4J.
The arrows on the diagram, some of which are marked with text, require additional packages for conversion, and some of which are not marked with text, are built-in adaptation implementations. In fact, the built-in implementation is more troublesome, because if you encounter coexistence, you will need to specify a logging implementation by configuring environment variables/configuring additional properties.
Slf4j is currently the core framework of the adaptation, and is the central hub of this diagram. As long as you adapt/transform around SLF4J, there are no conflicts that can’t be handled
conclusion
Solving the logging framework coexistence/conflict problem is simple, just follow a few principles:
- Unified use of a set of logging implementation
- Remove unwanted log dependencies
- If references must coexist, remove the original package and use a package of type “over” (the package of type “over” copies the original interface and reimplements it).
- Cannot be over, using the log abstraction provided by the specified way, for example
jboss-logging
Medium, yesorg.jboss.logging.provider
The environment variable specifies a specific logging framework implementation
After unifying the log framework in the project, no matter which log framework is used for printing, it will eventually be the only log framework after our transfer/adaptation.
With coexistence/conflict resolved, there is only one logging framework left in the project. There will no longer be “log typing”, “log configuration does not take effect” and other disgusting problems, work can be earlier!
Original is not easy to reprint, please at the beginning of the famous article source and author. If my article helped you, please like/bookmark/follow it ❤❤❤❤❤❤