Mybatis日志模块

前言

良好的日志在一个软件中占了非常重要的地位,日志是开发与运维管理之间的桥梁。日志可以帮助运维人员和管理人员快速查找系统的故障和瓶颈,也可以帮助开发人员与运维人员沟通,更好地完成开发和运维任务。但日志的信息量会随着软件运行时间不断变多,所以需要定期汇总和清理,避免影响服务器的正常运行。
在 Java 开发中常用的日志框架有 Log4j、Log4j2、Apache Commons Log、java.util.logging、slf4j等,这些工具对外的接口不尽相同。为了统一这些工具的接口,MyBatis 定义了一套统一的日志接口供上层使用,并为上述常用的日志框架提供了相应的适配器。

日志适配器

多种第三方日志组件都有各自的 Log级别,且都有所不同。例如java.util.logging提供了 All、FINEST、FINER、FINE、CONFIG、INFO、WARNING 等9种级别,而 Log4j2 则只有 trace、debug、info、warn、error、fatal这6种日志级别。MyBatis 统一提供了 trace、debug、warn、enor 四个级别,这基本与主流日志框架的日志级别类似,可以满足绝大多数场景的日志需求。
MyBatis 的日志模块位于 org.apache.ibatis.logging 包中,该模块中通过 Log接口定义了日志模块的功能,当然日志适配器也会实现此接口。

LogFactory 工厂类负责创建对应的日志组件适配器,类结构如下所示:

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(LogFactory::useSlf4jLogging);
    tryImplementation(LogFactory::useCommonsLogging);
    tryImplementation(LogFactory::useLog4J2Logging);
    tryImplementation(LogFactory::useLog4JLogging);
    tryImplementation(LogFactory::useJdkLogging);
    tryImplementation(LogFactory::useNoLogging);
  }

  private LogFactory() {
    // disable construction
  }

  public static Log getLog(Class<?> clazz) {
    return getLog(clazz.getName());
  }

  public static Log getLog(String logger) {
    try {
      return logConstructor.newInstance(logger);
    } catch (Throwable t) {
      throw new LogException("Error creating logger for logger " + logger + ".  Cause: " + t, t);
    }
  }

  public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
    setImplementation(clazz);
  }

  public static synchronized void useSlf4jLogging() {
    setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
  }

  public static synchronized void useCommonsLogging() {
    setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class);
  }

  /**
   * @deprecated Since 3.5.9 - See https://github.com/mybatis/mybatis-3/issues/1223. This method will remove future.
   */
  @Deprecated
  public static synchronized void useLog4JLogging() {
    setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
  }

  public static synchronized void useLog4J2Logging() {
    setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);
  }

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

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

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

}

LogFactory通过tryImplementation方法,依次尝试加载日志组件适配器,加载顺序如下:

  1. useSlf4jLogging :Slf4j
  2. useCommonsLogging :JCL
  3. useLog4J2Logging :Log4j2
  4. useLog4JLogging : Log4j
  5. useJdkLogging : JUL
  6. useNoLogging

日志简介

  • 日志门面:Slf4j、JCL、Log4j2;只提供日志相关的接口定义,即相应的 API,而不提供具体的接口实现。日志门面在使用时,可以动态或者静态地指定具体的日志框架实现,解除了接口和实现的耦合,使用户可以灵活地选择日志的具体实现框架。
  • 日志系统:Logback、Log4j、Log4j2、JUL等;与日志门面相对,它提供了具体的日志接口实现,应用程序通过它执行日志打印的功能。

PS :  Log4j2即可以作为日志门面,也可以作为日志的具体实现,一般用作日志的具体实现

Slf4j 

Slf4j的日志绑定和日志桥接,如下图所示:

日志绑定

日志桥接

JCL

JCL 全称为Jakarta Commons Logging,是Apache提供的一个通用日志API。它适配的日志实现:Log4j、JDK自带的日志(JUL)

相关官网地址

Mybatis使用各种日志

代码准备

创建mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <environments default="test">
        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper class="com.ys.mybatis.log.mapper.LogMapper"/>
    </mappers>

</configuration>
创建LogMapper
public interface LogMapper {

    @Select("select 1 from dual")
    Long printLog();
}
执行测试方法
public class LogTest {

    private SqlSessionFactory sqlSessionFactory;

    @BeforeEach
    public void init() throws IOException {
        InputStream inputStream = LogTest.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
        sqlSessionFactory = factoryBuilder.build(inputStream);
    }

    @Test
    public void testLog() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        LogMapper mapper = sqlSession.getMapper(LogMapper.class);
        mapper.printLog();
    }
}

Slf4j + Logback

添加pom依赖
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.12</version>
</dependency>

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.5.6</version>
</dependency>
 创建logback.xml
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <property name="pattern" value="logback %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>

    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${pattern}</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="console"/>
    </root>

</configuration>

PS :日志格式以logback开头 

执行测试方法

Slf4j + Log4j2

添加pom依赖
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.12</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
    <version>3.2.1</version>
</dependency>
创建log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">

    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="log4j2 %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>

    <Loggers>
        <Root level="debug">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>

</Configuration>

PS :日志格式以log4j2开头 

执行测试方法

JCL + Log4j

添加pom依赖
<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
创建log4j.properties
log4j.rootLogger = debug,console
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern = log4j [%.10p] %t %d{yyyy-MM-dd HH:mm:ss} %m%n

PS :日志格式以log4j开头

执行测试方法

JUL

创建jul.properties
handlers= java.util.logging.ConsoleHandler

.level= ALL

# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
修改@BeforeEach标记的init()方法
@BeforeEach
public void init() throws IOException {
    ClassLoader classLoader = LogTest.class.getClassLoader();

    InputStream inputStream = classLoader.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
    sqlSessionFactory = factoryBuilder.build(inputStream);

    InputStream logInputStream = classLoader.getResourceAsStream("jul.properties");

    LogManager logManager = LogManager.getLogManager();
    logManager.readConfiguration(logInputStream);
}
执行测试方法

  • 23
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值