[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-XSJ80gT0-1630940490789)(I:\SpringBoot unified log processing \ img\slf4j log output process. png)]
As can be seen from the figure, after the application calls the api interface of slf4j, the specific implementation is to find the corresponding log system through the slf4j log interface to realize log output
Solve the inconsistency of multi framework logs
Now let's go back to the issue of log unification. We have already learned that the frameworks used for developing common frameworks, such as Spring and mybatis, are all chosen by the framework developers. If we introduce a log system into each framework and finally need to print logs, n log system platforms will be used, and each log printing format Both content and performance need manual control, which not only makes the project larger, but also increases the complexity of the project and has a great impact on the performance. So how can we make all open source frameworks use slf4j to output uniformly? Let's take a look at the slf4j official scheme, as shown in the figure:
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-pLRgkwyt-1630940490791)(I:\SpringBoot unified log processing \ img\sfl4j adaptation log. png)]
As can be seen from the figure, the official solution is to develop a set of adaptive and compatible frameworks for different logging frameworks. These compatible jars can be used to replace the original logging framework, such as log4j logging framework, which corresponds to log4j-over-slf4j.jar, and the common logging framework, The slf4j team has implemented a set of compatible frameworks based on slf4j. The relationship is as follows:
Log frame | slf4j compatible framework |
---|---|
log4j | log4j-over-slf4j |
commons logging | jcl-over-slf4j |
java.util.logging | jui-to-slf4j |
How does SpringBoot handle log relationships
When using SpringBoot, we will find that the official default is spring ‐ boot ‐ starter ‐ logging, which is used to introduce the logging system. We expand the dependency graph of the dependency, as follows:
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-rHhD6wis-1630940490793)(I:\SpringBoot unified log processing \ img\SpringBoot processing log relationship. png)]
It can be seen that the spring ‐ boot ‐ starter ‐ logging starter introduces the dependency of four log instances, namely, the dependency of logback and log compatible jar mentioned earlier, and finally introduces the dependency of slf4j's log facade to realize unified log processing. But why can the problem of log output be solved after the introduction of compatible jar? Is there any magical black technology in the compatible package? In fact, it is not. Let's randomly expand the package names of several compatible log jars, as shown in the figure:
[the external link image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-qWI42k88-1630940490795)(I:\SpringBoot unified log processing \ img \ package name relationship of log compatible package. png)]
The package names of these log compatible packages are exactly the same as those of the original log framework, and a set of API s as before are implemented in full accordance with slf4j. In this way, the open source framework that relies on these log frameworks will not report an error when looking up the class under the corresponding package name, but those familiar with the java class loading mechanism know it, If the package names and classes of the two jar s are the same, the loading will be abnormal. We go into the pom dependency of spring ‐ boot ‐ starter ‐ logging to find out. Finally, we find clues in the maven dependency. For example, the spring framework uses commons logging, while in spring boot starter logging, we exclude the log dependency of spring, as follows:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring‐core</artifactId> <exclusions> <exclusion> <groupId>commons‐logging</groupId> <artifactId>commons‐logging</artifactId> </exclusion> </exclusions> </dependency>
In this way, when the spring framework is used at runtime, it uses the log instance in the compatible jar. SpringBoot successfully completed a unified stealing operation of the log system.
Bridging principle of slf4j
By viewing the log processing of SpringBoot, we can roughly summarize the following steps:
1. Exclude other log frames in the system first;
2. Replace the original log framework with a tundish;
3. We import slf4j other implementations
Through the above operations, we can complete the unification of the log system, but we began to have new doubts. How can slf4j automatically find the corresponding implementation log and complete the normal printing operation of the log? This involves the bridging principle of slf4j. Let's take a look at the code related to log calls in the slf4j source code:
//slf4j log calling procedure related code //Get log instance by name public static Logger getLogger(String name) { ILoggerFactory iLoggerFactory = getILoggerFactory(); return iLoggerFactory.getLogger(name); } //Obtain the log instance factory and complete the search and initialization of the log instance public static ILoggerFactory getILoggerFactory() { if (INITIALIZATION_STATE == UNINITIALIZED) { INITIALIZATION_STATE = ONGOING_INITIALIZATION; //Find implementation classes performInitialization(); } ... return StaticLoggerBinder.getSingleton().getLoggerFactory(); ... }
You can see that the whole process is to initialize the log factory through StaticLoggerBinder.getSingleton(), and where does the StaticLoggerBinder class come from? We found that the StaticLoggerBinder class does not exist in the jar of slf4j, but finds the specific implementation class by finding the path of org/slf4j/impl/StaticLoggerBinder.class. The code is as follows:
//Set the default StaticLoggerBinder path to find log instances private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class"; private static Set findPossibleStaticLoggerBinderPathSet() { ....... paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH); ...... }
At this time, we should think about a question. If we have multiple StaticLoggerBinder at the same time, which one will be loaded? Familiar with the java class loading mechanism, the class loader will scan the jar package directory one by one and load it in a certain order, so the StaticLoggerBinder scanned by the class loader first will be loaded first. The specific loading order is as follows:
1.$java_ Java core api in home / lib directory
2.$java_ Java extension jar package in home / lib / ext directory
3. Classes and jar packages in the directory indicated by Java - classpath / - djava.class.path
4.$CATALINA_ In the home / common directory, load from top to bottom in the order of folders
5.$CATALINA_ In the home / server directory, the folders are loaded from top to bottom
6.$CATALINA_ The base / shared directory is loaded from top to bottom in the order of folders
7. class document under project / WEB-INF/classes
8. jar file under project / WEB-INF/lib
Transform logger according to slf4j bridging principle
We all know that when using slf4j to output logs, we often obtain Logger instances for log printing. However, Logger only supports local logs and does not support logs in distributed environments. However, there are LogBean instances in slfj, which can support distributed logs and contain link related information. Can we transform the bridging process of slf4j, So that we can flexibly use local logs or distributed logs? First, let's look at the requirements we need to implement:
- logger and logbean are combined to unify the log entry
- Logbeans reduce code intrusion
- Seamlessly replace the logs in the third-party framework and add them to the distributed logs according to requirements
To realize this function, there are two ideas:
1. We can output local logs and distributed logs respectively by customizing the appender and extending the appender based on logback. However, the disadvantage is that the extendability of the appender is not high and many parameter information can not be obtained, such as context information
2. We implement the Logger interface to aggregate the Logger and LogBean together, so as to integrate the LogBean into the Logger. Similarly, the defect of this method is that there is nothing we can do about the third-party framework log, we can't directly replace it, and we need to use a custom LogFactory when using it
We can see from the first idea that the limitations are too high and the flexibility is not enough. Next, we try to use the second scheme to aggregate the Logger and LogBean, and disclose the unified api for log output:
public class CustomLogger implements LocationAwareLogger { private Logger logger; //Provide the getLogger method to get the logger public static LoggerFacade getLogger(Class clazz) { LoggerFacade loggerFacade = new LoggerFacade(); loggerFacade.logger = LoggerFactory.getLogger(clazz); return loggerFacade; } ... //Print the local log and output it to the log bean at the same time @Override public void warn(String msg) { logger.warn(msg); appendExtra(msg, Level.WARN); } ...... public void appendExtra(String str, Level level) { String date = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"); //Get the context. Judge by the context. If it exists, get the LogBean instance of the distributed environment ThreadContext threadContext = ContextContainer.retrieveServiceContext(); if (threadContext != null) { LogBean logBean = threadContext.getLogBean(); if (logBean != null) { logBean.getInner().getExtra().add(date + " " + level.toString() + " " + simpleName(getName()) + " -" + " " + str); } } ## Share readers > Author 2013 java go to Android Development, worked in small factories, and went to Huawei, OPPO After staying in a big factory, he entered Ali in April of 18 and has been here until now. > I have been interviewed and interviewed many people. I know most junior and intermediate students Android Engineers who want to improve their skills often grope and grow by themselves. The learning effect of fragmentation is inefficient and long, and it is very easy to encounter the stagnation of ceiling technology! We sorted out a copy, Ali P7 Rank Android A complete set of learning materials for architects, especially suitable for 3-5 Small partners with more than years of experience will learn and improve in depth. It mainly includes Mainstream Architecture Technologies of first-line Internet companies such as Alibaba, byte beating, Tencent, Huawei and Xiaomi. If you need it, just take it away. ![](https://img-blog.csdnimg.cn/img_convert/206ab4ae1dc1a495ed28db46311d05a1.png) **[CodeChina Open source projects:< Android Summary of study notes+Mobile architecture video+Real interview questions for large factories+Project practice source code](https://codechina.csdn.net/m0_60958482/android_p7)** 35 The middle-aged crisis is mostly due to being led by short-term interests and squeezing out value too early. If you can establish a correct long-term career plan at the beginning. After the age of 35, you will only be more valuable than the people around you. Purpose:< Android Summary of study notes+Mobile architecture video+Real interview questions for large factories+Project practice source code](https://codechina.csdn.net/m0_60958482/android_p7)** 35 The middle-aged crisis is mostly due to being led by short-term interests and squeezing out value too early. If you can establish a correct long-term career plan at the beginning. After the age of 35, you will only be more valuable than the people around you.