mybatis 日志加载

mybatis 日志处理

为了避免歧义,这里附上分析的版本号

名称 版本
java 1.8.0_102
mybatis 3.4.6

环境

maven

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

例子

    public class LogTest {
        public static void main(String[] args) {
            org.apache.ibatis.logging.Log log = org.apache.ibatis.logging.LogFactory.getLog(LogTest.class);
            log.debug("debug message");
        }
    }

控制台没有任何输出.

分析加载过程

首先分析 org.apache.ibatis.logging.LogFactory 类加载时调用的静态区域

static {
    tryImplementation(() -> {useSlf4jLogging(););
    tryImplementation(() -> {useCommonsLogging(););
    tryImplementation(() -> {useLog4J2Logging(););
    tryImplementation(() -> {useLog4JLogging(););
    tryImplementation(() -> {useJdkLogging(););
    tryImplementation(() -> {useNoLogging(););
  }

按照 slf4j -> commons -> log4j2 -> log4j -> jdklog -> nolog 去加载日志.

名称 类别 描述
slf4j 接口 可以自由配置: log4j,logback,log4j2 等日志实现
commons-logging 接口 可以自由配置: log4j,logback,log4j2 等日志实现
log4j2 实现 log4j的第二代进化版本
log4j 实现 log4j第一代
jdklog 接口 配置jdk自带的 控制台,文件 或自定义日志
nolog 实现 所有日志打印均不作任何操作

由于目前没有配置任何和日志有关的信息,所以这里一定会加载到 jdklog

接下来逐步分析每个类型的加载过程

slf4j

    public static synchronized void useSlf4jLogging() {
        setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.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);
      }
    }

实际上就是创建 new Slf4jImpl(“org.apache.ibatis.logging.LogFactory”);

创建实现类

源码中,是import 的 org.slf4j.Logger。 这里为了便于理解,直接加载类前面

public class Slf4jImpl implements org.apache.ibatis.logging.Log {

  private org.apache.ibatis.logging.Log log;

  public Slf4jImpl(String clazz) {
    org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(clazz);

    if (logger instanceof org.slf4j.LocationAwareLogger) {
      try {
        // check for slf4j >= 1.6 method signature
        logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class, Throwable.class);
        log = new org.slf4j.Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger);
        return;
      } catch (SecurityException e) {
        // fail-back to Slf4jLoggerImpl
      } catch (NoSuchMethodException e) {
        // fail-back to Slf4jLoggerImpl
      }
    }

    // Logger is not LocationAwareLogger or slf4j version < 1.6
    log = new org.slf4j.Slf4jLoggerImpl(logger);
  }
  // 省略其他无关的方法
}

使用适配器模式, 实现 org.apache.ibatis.logging.Log 接口,内部调用 org.slf4j.LoggerFactory.getLogger 的方法

这里分两种情况
1. 找到 LoggerFactory 和 Logger 类, 创建代理对象 log = = new org.slf4j.Slf4jLoggerImpl(logger);
2. 未找到 LoggerFactory 和 Logger 类, 抛出 java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory, 然后被最顶层的 tryImplementation 捕获后忽略

这里说找到 LoggerFactory 和 Logger 类, 而不是说引入slf4j的jar包,

表示我们可以自己建立这两个类, 来替代实现. (不推荐, 引入jar包就行)

后面的内容都相同,直接附上不同的代理类和代理对象

commons-logging

  • 代理类: org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class
  • 代理对象: org.apache.commons.logging.Log

log4j

  • 代理类: org.apache.ibatis.logging.log4j.Log4jImpl.class
  • 代理对象: org.apache.log4j.Logger

log4j2

  • 代理类: org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class
  • 代理对象: org.apache.logging.log4j.Logger

jdk

  • 代理类: org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class
  • 代理对象: java.util.logging.Logger

加载流程图

mybatis日志流程图

使用

log4j

添加maven配置

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>

添加 log4j.properties

log4j.rootLogger=TRACE, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n

控制台输出:

11:49:19,636 DEBUG main logging.LogFactory:135 - Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
11:49:19,638 DEBUG main aya.LogTest:6 - debug message

slf4j+log4j12

maven

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.11.0</version>
        </dependency>

log4j-slf4j-impl 2.11.0 会自动引入 slf4j 1.8.0-alpha2 和log4j 2.11.0 的依赖

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="DEBUG">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

运行程序,控制台输出:

12:19:01.191 [main] DEBUG com.aya.LogTest - debug message

总结

  1. 自动查找顺序: slf4j -> commons -> log4j2 -> log4j -> jdklog -> nolog

可以在settings 设置 logImpl 指定以下的其中一种,未指定时自动查找
1. SLF4J
2. LOG4J
3. LOG4J2
4. JDK_LOGGING
5. COMMONS_LOGGING
6. STDOUT_LOGGING
7. NO_LOGGING

资料

http://www.mybatis.org/mybatis-3/zh/configuration.html#settings

猜你喜欢

转载自blog.csdn.net/mz4138/article/details/81001131