Analysis of MyBatis Log Module

Keywords: Programming Apache Mybatis log4j JDK

Label (Space Separation): Uncategorized

The package where the log function code resides

org.apache.ibatis.logging

Loading order of log module

mybatis does not have its own log module. It uses third-party logs (and jdk has its own logs) Log loading order slf4J_commonsLoging_Log4J2_Log4J_JdkLog

Related code

Declare the loading order in the static code block

public final class LogFactory {
  //Constructing the selected third-party log component adapter
  private static Constructor<? extends Log> logConstructor;

  /**
   * Marker to be used by logging implementations that support markers
   */
  public static final String MARKER = "MYBATIS";

  //Construction Method of Selected Third Party Log Component Adapter
  private static Constructor<? extends Log> logConstructor;

  //Automatic scanning log implementation, and third-party log plug-in loading priority is as follows: slf4J commonsLoging Log4J2 Log4J JdkLog
  static {
    tryImplementation(LogFactory::useSlf4jLogging);
    tryImplementation(LogFactory::useCommonsLogging);
    tryImplementation(LogFactory::useLog4J2Logging);
    tryImplementation(LogFactory::useLog4JLogging);
    tryImplementation(LogFactory::useJdkLogging);
    tryImplementation(LogFactory::useNoLogging);
  }

Here we use the grammatical sugar of Java 8 The tryImplementation method requires a parameter of type runnable, which is annotated with @Functional Interface, so you can use lambda grammar to abbreviate it. LogConstructor preserves the method of constructing log objects, then creates objects by reflection. When logConstructor == null, the run method calling running is used, then what is run's content?

tryImplementation(LogFactory::useSlf4jLogging);

private static void tryImplementation(Runnable runnable) {
    if (logConstructor == null) {//When a construction method is not executed by a vacant person
      try {
        runnable.run();
      } catch (Throwable t) {
        // ignore
      }
    }
  }

LogFactory::useSlf4jLogging The setImplementation method attempts to create a Class (get the constructor by reflection) by passing in lass. If successful, save the constructor to the logConstructor for later use.

public static synchronized void useSlf4jLogging() {
    setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
  }
  
  
  //Initialize the construction method by specifying the log class
  private static void setImplementation(Class<? extends Log> implClass) {
    try {
      Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
      Log log = candidate.newInstance(LogFactory.class.getName());
      if (log.isDebugEnabled()) {
        log.debug("Logging initialized using '" + implClass + "' adapter.");
      }
      logConstructor = candidate;
    } catch (Throwable t) {
      throw new LogException("Error setting Log implementation.  Cause: " + t, t);
    }
  }

The design pattern adapter pattern used here is different because different log components have different definitions of log info error s and so on. mybatis unifies them into the following levels

package org.apache.ibatis.logging;
public interface Log {
  boolean isDebugEnabled();
  boolean isTraceEnabled();
  void error(String s, Throwable e);
  void error(String s);
  void debug(String s);
  void trace(String s);
  void warn(String s);

Let's look at the implementation class of the highest priority log slf4J

package org.apache.ibatis.logging.log4j;

import org.apache.ibatis.logging.Log;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

/**
 * @author Eduardo Macarron
 */
public class Log4jImpl implements Log {
  private static final String FQCN = Log4jImpl.class.getName();
  private final Logger log;
  public Log4jImpl(String clazz) {
    log = Logger.getLogger(clazz);
  }
  @Override
  public boolean isDebugEnabled() {
    return log.isDebugEnabled();
  }
  @Override
  public boolean isTraceEnabled() {
    return log.isTraceEnabled();
  }
  @Override
  public void error(String s, Throwable e) {
    log.log(FQCN, Level.ERROR, s, e);
  }
  @Override
  public void error(String s) {
    log.log(FQCN, Level.ERROR, s, null);
  }
  @Override
  public void debug(String s) {
    log.log(FQCN, Level.DEBUG, s, null);
  }
  @Override
  public void trace(String s) {
    log.log(FQCN, Level.TRACE, s, null);
  }
  @Override
  public void warn(String s) {
    log.log(FQCN, Level.WARN, s, null);
  }
}

The Embodiment of Logging Function

MyBatis enhances Connection,PreparedStatement,ResultSet through dynamic proxy. Its corresponding implementation is the xxxLogger class.

Posted by TecTao on Wed, 02 Oct 2019 13:50:26 -0700