mybatis源码(五)mybatis日志实现

mybatis源码(五)mybatis日志实现

日志在项目中很常见,能够记录系统的运行状态,有助于开发人员排查系统bug,我们使用什么样的日志,在maven的pom中添加相应的依赖,就可以使用了,那么mybatis是怎么做的呢

mybatis通过Log接口定义了日志规范

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);

}

mybatis针对不同的日志框架提供了不同的实现  

实现类中的代码都比较简单,都是调用对应日志框架的api来打印日志。例如:

public class Log4j2Impl implements Log {

  private final Log log;

  public Log4j2Impl(String clazz) {
    Logger logger = LogManager.getLogger(clazz);

    if (logger instanceof AbstractLogger) {
      log = new Log4j2AbstractLoggerImpl((AbstractLogger) logger);
    } else {
      log = new Log4j2LoggerImpl(logger);
    }
  }

  @Override
  public boolean isDebugEnabled() {
    return log.isDebugEnabled();
  }

  @Override
  public boolean isTraceEnabled() {
    return log.isTraceEnabled();
  }

  @Override
  public void error(String s, Throwable e) {
    log.error(s, e);
  }

  @Override
  public void error(String s) {
    log.error(s);
  }

  @Override
  public void debug(String s) {
    log.debug(s);
  }

  @Override
  public void trace(String s) {
    log.trace(s);
  }

  @Override
  public void warn(String s) {
    log.warn(s);
  }

}

mybatis支持7中不同的日志策略。在LogFactory中,如下所示

// 自定义日志实现
  public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
    setImplementation(clazz);
  }
  //slf4j框架输出日志
  public static synchronized void useSlf4jLogging() {
    setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
  }
  // JCL框架输出日志
  public static synchronized void useCommonsLogging() {
    setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class);
  }
  // Log4j框架输出日志
  public static synchronized void useLog4JLogging() {
    setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
  }
  // Log4j2框架输出日志
  public static synchronized void useLog4J2Logging() {
    setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);
  }
  // JUL框架输出日志
  public static synchronized void useJdkLogging() {
    setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
  }
  // 使用标准输出设备输出日志
  public static synchronized void useStdOutLogging() {
    setImplementation(org.apache.ibatis.logging.stdout.StdOutImpl.class);
  }
  // 不输出日志
  public static synchronized void useNoLogging() {
    setImplementation(org.apache.ibatis.logging.nologging.NoLoggingImpl.class);
  }
setImplementation 方法指定日志实现类
  private static void setImplementation(Class<? extends Log> implClass) {
    try {
      // 获取日志实现类的Constructor对象
      Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
      // 根据日志实现类创建Log实例
      Log log = candidate.newInstance(LogFactory.class.getName());
      if (log.isDebugEnabled()) {
        log.debug("Logging initialized using '" + implClass + "' adapter.");
      }
      // 记录当前使用的日志实现类的Constructor对象
      logConstructor = candidate;
    } catch (Throwable t) {
      throw new LogException("Error setting Log implementation.  Cause: " + t, t);
    }
  }

使用mybatis时,我们如果不指定使用哪种日志框架,mybatis能够按照顺序从classpath下查找日志框架的jar包。如果classpath下面有对应日志的jar包,则使用该框架打印日志

mybatis动态查找日志框架的代码是在LogFactory中的static代码块中完成的

public final class LogFactory {

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

  private static Constructor<? extends Log> logConstructor;

  static {
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useSlf4jLogging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useCommonsLogging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useLog4J2Logging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useLog4JLogging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useJdkLogging();
      }
    });
    tryImplementation(new Runnable() {
      @Override
      public void run() {
        useNoLogging();
      }
    });
  }

    private static void tryImplementation(Runnable runnable) {
    if (logConstructor == null) {
      try {
        runnable.run();
      } catch (Throwable t) {
        // ignore
      }
    }
  }

static类加载的时候,首先加载 useSlf4jLogging()方法。表示使用SLF4J框架。然后从classpath中查找相关的jar包,如果classpath下不存在相关的jar包,则useSlf4jLogging()方法就会抛出异常ClassNotFoundException等异常。这个异常会被catch捕获。捕获完成后,并不做任何处理继续扫描其他的类型的日志框架。继续寻找下一个日志框架的过程中,如果存在相应的日志jar包,则使用对应的日志框架类输出日志。如果日志框架中不存在任何框架的日志包,则使用NoLoggintImpl()的实现类。扫描的顺序为SLF4J--->JCL--->Log4j2--->Log4j--->JUL--->NoLogging

mybatis支持在mybatis-config主配置文件中,指定使用哪种框架

<configuration>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

属性值支持使用别名的方式,这是因为在Configuration的构造方法中,添加了别名的注册

 // 日志框架的别名
    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

mybatis中的Configuration对象中,还维护了如下代码

  protected Class<? extends Log> logImpl;

  public Class<? extends Log> getLogImpl() {
    return logImpl;
  }

  public void setLogImpl(Class<? extends Log> logImpl) {
    if (logImpl != null) {
      this.logImpl = logImpl;
      // 调用LogFactory类的useCustomLogging()方法指定日志实现类
      LogFactory.useCustomLogging(this.logImpl);
    }
  }

在解析主配置文件的时候,是通过如上代码指定日志框架

在Configuration对象中,通过setLogImpl方法调用 LogFactory.useCustomLogging(this.logImpl); 来指定日志实现类的而mybaits中所有的日志实例是通过LogFactory创建的。这就保证了整个日志的输出框架使用的同一种日志

 

  

原文地址:https://www.cnblogs.com/yingxiaocao/p/13554262.html