Log4j-over-slf4j and slf4j-log4j12 co-exist Stack Overflow exception Analysis

Source: Internet
Author: User

Note: The words "bridging", "transfer", and "binding" in the following sections are basically the same concept.

The log4j-over-slf4j and slf4j-log4j12 are two jar packages related to the Java logging system, which may cause a stack overflow exception when both of them appear under the classpath. The exception information is roughly as follows (from the slf4j official documentation detected both log4j-over-slf4j.jar and slf4j-log4j12.jar on the class path, preempting stackoverflowerror ):

Exception in thread "main" java.lang.StackOverflowError  at java.util.Hashtable.containsKey(Hashtable.java:306)  at org.apache.log4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:36)  at org.apache.log4j.LogManager.getLogger(LogManager.java:39)  at org.slf4j.impl.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:73)  at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:249)  at org.apache.log4j.Category.<init>(Category.java:53)  at org.apache.log4j.Logger..<init>(Logger.java:35)  at org.apache.log4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:39)  at org.apache.log4j.LogManager.getLogger(LogManager.java:39)  at org.slf4j.impl.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:73)  at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:249)  at org.apache.log4j.Category..<init>(Category.java:53)  at org.apache.log4j.Logger..<init>(Logger.java:35)  at org.apache.log4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:39)  at org.apache.log4j.LogManager.getLogger(LogManager.java:39)  subsequent lines omitted...

Existing Log System

Before analyzing the specific causes of this exception, it is necessary to quickly understand the existing Java log system. Is a diagram of the existing Java log system:

It is not very accurate, but it can clearly display the main architecture of the existing Java log system. The Java log system can be divided into three parts: log facade interface, bridge, and log Framework implementation.

There are many Java log frameworks, the simplest of which is Java. util. logging, and the most classic one is log4j. Later, a logback with better performance than log4j emerged. Other log frameworks are not very commonly used. Applications can directly use the APIs of these specific log frameworks to meet log output requirements. However, because the APIs of different log frameworks are generally incompatible, this makes the application lose the flexibility to change the log framework.

A more reasonable choice than using a specific log framework API is to use the log facade interface. The log facade interface provides a set of Apis independent of the specific log framework. Applications can use these independent APIs to decouple the APIs from the specific log framework, which is similar to JDBC. The earliest log facade interface was commons-logging, but currently the most popular is slf4j.

The log facade interface does not have the actual log output capability. The underlying layer still needs to call the specific log framework API, that is, it needs to be used in combination with the specific log framework. Because there are many log frameworks and most of them are not compatible with each other, the log facade interface may need a corresponding bridge to be combined with any log framework, it is like the JDBC driver corresponding to the combination of JDBC and various databases.

It should be noted that, as mentioned above, it is not accurate. This is only the main part. The actual situation is not always a one-way line of "log facade interface --> bridge --> log framework. In fact, an independent bridge is sometimes not required, and it is not just a bridge that transfers the log facade API to a specific log framework API, there is also a bridge that transfers the log framework API to the log facade API.

To put it bluntly, the so-called "bridge" is just a pseudo implementation of a set of APIs. This implementation does not directly complete the functions declared by the API, but calls other APIs with similar functions. This completes the transition from "an API" to "another API. If both the A-to-B.jar and the B-to-A.jar are present, you can imagine what happens when the application begins to call an API of A or B. This is the basic principle of the stack overflow exception.

Slf4j transfer binding

The above is just a general introduction of the existing Java log system, but it cannot be detailed description of the problem, you need to learn more about the bridge between slf4j and a specific log framework.

Slf4j bridge to specific log framework

From the official slf4j documentation binding with a logging framework at deployment time:

We can see that slf4j is combined with a specific log framework in many ways. Of course, the top layers of each solution (the Green Application Layer) are unified, and they are directly calling the API provided by slf4j (light blue abstract API layer), depending on the slf4j-api.jar. Then, slf4j API is very free to do further, and almost all the specific log frameworks can be used. Note that the second layer in the figure is light blue. The legend in the lower left corner shows that this indicates the abstract log API, which is not a specific implementation. If the lower layer is not combined with any specific log Framework implementation as in the first solution on the left, logs cannot be output (it is not sure whether the log may be output to the standard output by default ).

In the figure, the third layer is obviously not as uniform as the first and second layers, because the specific log framework has been involved here.

First, let's look at the two blue blocks in the middle of the third layer. This is the adaptation layer, that is, the bridge. The slf4j-log4j12.jar Bridge on the left to see the name is known to be slf4j to log4j bridge, similarly, the right slf4j-jdk14.jar is slf4j to Java Native log Implementation of the bridge. Their next layer is the corresponding log Framework implementation. The implementation code of log4j is log4j. jar, and the Jul implementation code is included in the JVM runtime without a separate jar package.

Let's look at the remaining three dark blue color blocks on the third layer. These three are also specific log framework implementations, but they do not need a bridge, because they have directly implemented the slf4j API. Slf4j-simple.jar and slf4j-nop.jar these two needless to say, look at the name will know a simple implementation of slf4j, one is slf4j empty implementation, usually not very useful. Logback implements the slf4j API because logback and slf4j come from the same person. This person is also the author of log4j.

All the gray jar packages on the third layer have a red box, which means they all directly implement the slf4j API, but the implementation of the slf4j API by the lake blue bridge does not directly output logs, instead, call the APIs of other log frameworks.

Other log framework API forwarding back to slf4j

If only the above bridges from sfl4j to other log frameworks exist, there may be no problems. However, there is actually another kind of bridge. Their functions are just the opposite. They transfer the APIs of other log frameworks to the slf4j API. From the official slf4j documentation bridging legacy APIs:

Demonstrate all three situations so far that slf4j can be securely transferred back from other log framework APIs.

Take the first case in the upper left corner as an example. When the slf4j underlying bridge is connected to the logback framework, the upper layer allows the bridge back-to-slf4j log framework APIs including log4j and Jul. Although jcl is not a specific implementation of log frameworks, its APIs can still be transferred back to slf4j. To achieve the conversion, replace the original log framework jar with the specific bridge jar listed in the figure. It should be noted that the conversion from logback API to slf4j API is not included here, because logback is the implementation of slf4j API.

After reading the three cases, we will find that almost all APIs of other log frameworks, including jcl APIs, can be freely transferred back to slf4j. However, there is a unique limit that the log framework that is switched back to slf4j cannot be the same as the log framework currently connected to slf4j. This restriction is designed to prevent A-to-B.jar and B-to-A.jar from appearing in the class path at the same time, resulting in a and B constantly recursively calling each other, and finally stack overflow. Currently, this restriction is not guaranteed by technical support, but only by developers. This is why only three reasonable methods should be emphasized on the slf4j official website.

At this point, the principle of the exception displayed at the beginning is basically clear. In addition, it can be seen that there may be a combination of similar exceptions not only log4j-over-slf4j and slf4j-log4j12, slf4j official site also pointed out another pair: jcl-over-slf4j.jar and slf4j-jcl.jar

Sample Code

The previous analysis is theoretical, the actual code even if the use of log4j-over-slf4j and slf4j-log4j12, it may not necessarily occur exceptions. The following code calls the slf4j API to output logs. The slf4j underlying bridge is connected to log4j:

package test;public class HelloWorld {public static void main(String[] args) {org.apache.log4j.BasicConfigurator.configure();org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(HelloWorld.class);logger.info("Hello World");}}

Configure the jar package on the classpath as (note that log4j is before the log4j-over-slf4j ):

In this case, running the test program can output logs normally without Stack Overflow exceptions. However, if you adjust the jar sequence on classpath:

When you run the test program again, an exception similar to the stack overflow exception at the beginning of this article appears. We can see the obvious cyclical repetition:

Sequence Chart Analysis

A detailed call process sequence diagram of stack overflow. Call 1.1, 1.1.1, and so on ...... At the end of 1.1.1.1.1.1 (the last call in the figure), we found that it is exactly the same as 1, and the subsequent process is exactly the same.

Note that the initial fuse is not only the loggerfactory shown in the figure. getlogger () is one type. There are several other direct calls in applications that can trigger stack overflow exceptions. For example, the first statement org is used to trigger exceptions in the previous sample code. apache. log4j. basicconfigurator. configure (), but the subsequent infinite recursive call processes are basically the same.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.