Random talk: how does the log portal slf4j integrate the log framework

I preface

This article discusses some simple things, mainly to understand what log portal is and how it is integrated

II Log portal

The Java Development Manual published by Ali has such a mandatory specification:

The API in the logging system (Log4j, Logback) can not be directly used in the application, but the API in the logging framework (SLF4J, JCL – Jakarta common logging) should be used. The logging framework of facade mode is used, which is conducive to the unification of maintenance and log processing methods of various classes.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Test.class);

So why?

What is a log portal?

Simple Logging Facade for Java (SLF4J), as a simple facade or abstraction of various logging frameworks (such as Java.util.Logging, logback, log4j), allows end users to insert the required logging framework during deployment.

To put it simply, the purpose of portal is to decouple. SLF4J is like an abstract class and does not pay attention to the specific implementation When using, it can also be used flexibly

III Integration mode

Class diagram

It can be seen that the core pre-processing is the initialization stage, which is used to load the corresponding plug-ins into. For analysis, take Spring as an example:

3.1 initialization phase

// S1: initiation of initialization
// The Log is also loaded in the LogFactory phase 
C- SpringApplication
private static final Log logger = LogFactory.getLog(SpringApplication.class);

// S2: LogFactory initiates the processing flow and loads the Adapter
getLog -> LogAdapter.createLog(name);

// S3: load specific processing classes in the logadapter
public static Log createLog(String name) {
    switch(logApi) {
    // The code is very simple and critical. It is created according to the type
    // It can also be seen here that there will be coupling when using the logging framework, so it is more reasonable to use the portal
    case LOG4J:
        return LogAdapter.Log4jAdapter.createLog(name);
    case SLF4J_LAL:
        return LogAdapter.Slf4jAdapter.createLocationAwareLog(name);
    case SLF4J:
        return LogAdapter.Slf4jAdapter.createLog(name);
    default:
        return LogAdapter.JavaUtilAdapter.createLog(name);
    }
}

// S4: call specific implementation classes (take Slf4j as an example)
C- org.slf4j.LoggerFactory # getILoggerFactory 
    1- performInitialization () : obtain LoggerFactoru -> S5 
        - bind() : Scan class
        - StaticLoggerBinder.getSingleton().getLoggerFactory() : Returns the final object
    2- iLoggerFactory.getLogger(name) : adopt Factory obtain Logger object 

// S5: binder binding concrete implementation class
C- org.slf4j.LoggerFactory # bind()
    ?- The whole process is mainly divided into the following steps
    1- Get implementation class 
        |- Set<URL> staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
    2- Multi binding verification : This is also the case if multiple versions or types are different log The reason for the reminder when the framework
        |- reportMultipleBindingAmbiguity
    3- Print actual binding type (You can see the final implementation class)
        |- reportActualBinding(staticLoggerBinderPathSet);

// S5-1: loading of specific execution classes
// STATIC_LOGGER_BINDER_PATH -> org/slf4j/impl/StaticLoggerBinder.class
Set<URL> staticLoggerBinderPathSet = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
    ?- Here is to load the implementation of different implementation frameworks by scanning packages

Supplement: compatibility mode

Since it is impossible to introduce actual processing classes in order to decouple, how does it integrate?
An additional introduction can be seen in logback:

jar:file:/D:/java/resource/repo/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class

It can be seen from here that in fact, this integration mode needs to be compatible with a third party For example, log4j

jar:file:/D:/java/resource/repo/org/slf4j/slf4j-log4j12/1.7.30/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class

3.2 actual call phase

In the actual call, we use loggerfactory getLogger(Test.class); When initiating, the final implementation Factory class will be obtained according to the above process, and then through its iloggerfactory Getlogger (name) gets the specific Logger object. At this time, the object is already an implementation class

summary

  • The real decoupling method is realized through LogFactory
  • When using Logger, the implementation class is actually used

IV Mybatis analogy

Of course, direct call is easy to understand, so if it is a framework, how to use this feature?

Take Mybatis as an example to see how to use slf4j + logback to realize SQL output

I wrote a document before, Viewing Mybatis process from Log We only need two steps to output the log:

// S1: configure Mybatis log mode
mybatis-plus:
    configuration:
        log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl

// S2: configure Logback    
<logger name="com.apache.ibatis" level="debug"/>
<logger name="java.sql.Connection" level="debug"/>
<logger name="java.sql.Statement" level="debug"/>
<logger name="java.sql.PreparedStatement" level="debug"/>
<logger name="com.gang.test.dao.mapper" level="debug"/>

The log impl selected here is Slf4jImpl, which implements the specific log forwarding class. Where is it implemented?

We know that the core processing class of Mybatis Log is BaseJdbcLogger, which provides many different Log implementation classes: StdOutImpl / Slf4jImpl, etc

If we choose StdOutImpl, we can only print to the console simply, so we usually use Slf4jImpl or Log4jImpl to print to the log and capture the log

// Core: BaseJdbcLogger handles the Logger, but it can be seen here that the Log is actually transmitted from the outside
public BaseJdbcLogger(Log log, int queryStack) 

// So where is the incoming place? 
In building MappedStatement When , Mybatis Will pass LogFactory yes Log Build

// S1: load Log implementation class
private static void setImplementation(Class<? extends Log> implClass) {
    // Get the actual Log processing class through constructor reflection
    Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
    Log log = candidate.newInstance(LogFactory.class.getName());
    logConstructor = candidate;
}

// S2 : LogFactory # getLog
public static Log getLog(String logger) {
    return logConstructor.newInstance(logger);
}

Mybatis adaptation

Unlike the business system, which has a clear purpose, it can be used slf4j at the beginning. For example, frameworks such as Mybatis cannot be used explicitly at the beginning. Therefore, the internal method of Mybatis is: try to deal with them all. If there are successful ones, don't deal with the later ones

C- LogFactory : Try loading different log frames in turn
static {
  tryImplementation(LogFactory::useSlf4jLogging);
  tryImplementation(LogFactory::useCommonsLogging);
  tryImplementation(LogFactory::useLog4J2Logging);
  tryImplementation(LogFactory::useLog4JLogging);
  tryImplementation(LogFactory::useJdkLogging);
  tryImplementation(LogFactory::useNoLogging);
}

Note that this is not directly related to the previously configured log impl. Log impl is processed in the following steps

// S1: attribute loading
C- org.apache.ibatis.session.Configuration
public void setLogImpl(Class<? extends Log> logImpl) {
  if (logImpl != null) {
    this.logImpl = logImpl;
    LogFactory.useCustomLogging(this.logImpl);
  }
}

// S2: override as user-defined processing class
public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
  setImplementation(clazz);
}

It can also be seen here that the processing of Mybatis is actually divided into two steps:

  • Load the log processing implementation classes in order
  • If you customize the log implementation class, you can override the logConstructor to implement custom processing

summary

Unlike the subjective reflection implementation class, or multiple frameworks jointly implement a more abstract interface, Slf4j needs the log framework to actively adapt to implement its StaticLoggerBinder class, and then use the specific framework class by scanning the package

Through the analysis of Mybatis, we also see the impact of different logging frameworks. We need to couple classes in the code to adapt to different logging frameworks

If it's just an internal business system, choosing Slf4j can bring many advantages. If it's an open source framework, it's also a good choice to use Mybatis, which is full-scale first and then customized

Author: ant black
Link: https://juejin.cn/post/7071626946626453534

Keywords: Java Spring Interview

Added by Gregghawes on Mon, 07 Mar 2022 09:11:55 +0200