springboot+mybatis解决日志不打印sql的问题

目录

第一步:首先处理pom.xml中的依赖关系,将日志的实现切换为logback;

第二步:修改mybatis-config.xml

第三步:看一下mybatis源代码BaseExceutor底层原因


mybatis用logback日志不显示sql的解决方法

之前学习过日志框架的集成和切换等使用,感觉这块没有太深的东西,知道即懂,无需理解。

公司的项目一直都是运行起来会打印所有执行的语句:

好处是:一旦出现问题方便排查;

坏处是:正常情况下,控制台一些无用的日志信息量多且密无重点,不方便日常的调试开发。

项目没有采用分布式的架构,总体的架构设计和风格较*TT,最近大佬看我比较闲,让我处理一个小问题:

项目有多个服务,相互推送消息,其中除一服务(假设monitor)外使用的日志实现用的是log4j,其他服务都是logback。项目架构使用的springboot,因此统一使用logback实现。(springboot底层选用slf4j+logback方式进行日志记录

第一步:首先处理pom.xml中的依赖关系,将日志的实现切换为logback;

    <!--日志-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <!--<exclusions>-->
                <!--<exclusion>-->
                    <!--<groupId>org.springframework.boot</groupId>-->
                    <!--<artifactId>spring-boot-starter-logging</artifactId>-->
                <!--</exclusion>-->
            <!--</exclusions>-->
        </dependency>

经查看jar包依赖关系,发现原将spring-boot-starter-logging依赖排除出去了,只需将这部分注释掉即可,也无需导入新的依赖。因为spring-boot-starter底层已经为我们导入了spring-boot-starter-logging的依赖,它底层又适配了log4j,jul,jcl等日志实现~

(Springboot把其他的日志替换成了slf4j)

    <dependencies>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jul-to-slf4j</artifactId>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>log4j-over-slf4j</artifactId>
		</dependency>
	</dependencies>

第二步:修改mybatis-config.xml

研究了一天,本来都要放弃这种实现方法啦,奈何一直心有挂念,就多试了几次:

原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>
      <!-- 将log实现设置为log4j.默认是jdk的log -->       
	  <setting name="logImpl" value="LOG4J"/>
      <!-- 使全局的映射器启用或禁用缓存。 -->        
      <setting name="cacheEnabled" value="true"/>        
      <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 -->        
      <setting name="lazyLoadingEnabled" value="true"/>        
      <!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。 -->        
      <setting name="aggressiveLazyLoading" value="true"/>        
      <!-- 是否允许单条sql 返回多个数据集  (取决于驱动的兼容性) default:true -->        
      <setting name="multipleResultSetsEnabled" value="true"/>        
      <!-- 是否可以使用列的别名 (取决于驱动的兼容性) default:true -->        
      <setting name="useColumnLabel" value="true"/>        
      <!-- 允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。  default:false  -->        
      <setting name="useGeneratedKeys" value="false"/>        
      <!-- 指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射 PARTIAL:部分  FULL:全部  -->        
      <setting name="autoMappingBehavior" value="PARTIAL"/>        
      <!-- 这是默认的执行类型  (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH: 执行器可以重复执行语句和批量更新)  -->        
      <setting name="defaultExecutorType" value="SIMPLE"/>        
      <!-- 使用驼峰命名法转换字段。 -->        
      <setting name="mapUnderscoreToCamelCase" value="true"/>        
      <!-- 设置本地缓存范围 session:就会有数据的共享  statement:语句范围 (这样就不会有数据的共享 ) defalut:session -->        
      <setting name="localCacheScope" value="SESSION"/>        
      <!-- 设置但JDBC类型为空时,某些驱动程序 要指定值,default:OTHER,插入空值时不需要指定类型 -->        
      <setting name="jdbcTypeForNull" value="NULL"/>    
   </settings>    
     <plugins>
       <!-- <plugin interceptor="com.github.pagehelper.PageHelper">-->
         <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <property name="offsetAsPageNum" value="false"/>            
            <property name="rowBoundsWithCount" value="false"/>
            <property name="pageSizeZero" value="true"/>
            <property name="reasonable" value="false"/>
            <property name="supportMethodsArguments" value="false"/>    
            <property name="returnPageInfo" value="none"/>
       </plugin>  
  </plugins>
</configuration>

重点注意:

<!-- 将log实现设置为log4j.默认是jdk的log,即JDK_LOGGING  -->       
<setting name="logImpl" value="LOG4J"/>

不错,打印SQL只需要加一个setting就可以了。

mybatis的日志打印方式比较多,SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING,可以根据自己的需要进行配置。

但是注意没有LOGBACK这一实现呦~,关于logimpl的设定值还不支持logback,如果用SLF4J是不好用的。这是官方文档的描述,见下图:

springboot+mybatis解决日志不打印sql的问题

依次将以上实现都试了试:

COMMONS_LOGGING |JDK_LOGGING(默认配置)或者不加此setting配置(相当于默认加了JDK_LOGGING配置):正确打印预期日志,符合要求

springboot+mybatis解决日志不打印sql的问题

STDOUT_LOGGING :没有问题,只是打印日志的格式不合心意,明明配置了打印的格式

springboot+mybatis解决日志不打印sql的问题

SLF4J | LOG4J | LOG4J2:配置报错

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.springframework.core.NestedIOException: Failed to parse config resource: class path resource [mybatis-config.xml]; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.logging.LogException: Error setting Log implementation.  Cause: java.lang.reflect.InvocationTargetException
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1173)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1067)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:513)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByType(AbstractAutowireCapableBeanFactory.java:1342)
	... 43 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.springframework.core.NestedIOException: Failed to parse config resource: class path resource [mybatis-config.xml]; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.logging.LogException: Error setting Log implementation.  Cause: java.lang.reflect.InvocationTargetException
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
	... 55 common frames omitted
Caused by: org.springframework.core.NestedIOException: Failed to parse config resource: class path resource [mybatis-config.xml]; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.logging.LogException: Error setting Log implementation.  Cause: java.lang.reflect.InvocationTargetException
	at org.mybatis.spring.SqlSessionFactoryBean.buildSqlSessionFactory(SqlSessionFactoryBean.java:499)
	at org.mybatis.spring.SqlSessionFactoryBean.afterPropertiesSet(SqlSessionFactoryBean.java:381)
	at org.mybatis.spring.SqlSessionFactoryBean.getObject(SqlSessionFactoryBean.java:546)
	at org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.sqlSessionFactory(MybatisAutoConfiguration.java:139)
	at org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration$$EnhancerBySpringCGLIB$$4e18cfb2.CGLIB$sqlSessionFactory$1(<generated>)
	at org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration$$EnhancerBySpringCGLIB$$4e18cfb2$$FastClassBySpringCGLIB$$1e5eda79.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
	at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:358)
	at org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration$$EnhancerBySpringCGLIB$$4e18cfb2.sqlSessionFactory(<generated>)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
	... 56 common frames omitted
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.logging.LogException: Error setting Log implementation.  Cause: java.lang.reflect.InvocationTargetException
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:120)
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:98)
	at org.mybatis.spring.SqlSessionFactoryBean.buildSqlSessionFactory(SqlSessionFactoryBean.java:493)
	... 69 common frames omitted
Caused by: org.apache.ibatis.logging.LogException: Error setting Log implementation.  Cause: java.lang.reflect.InvocationTargetException
	at org.apache.ibatis.logging.LogFactory.setImplementation(LogFactory.java:139)
	at org.apache.ibatis.logging.LogFactory.useCustomLogging(LogFactory.java:89)
	at org.apache.ibatis.session.Configuration.setLogImpl(Configuration.java:222)
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.settingsElement(XMLConfigBuilder.java:263)
	at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:113)
	... 71 common frames omitted
Caused by: java.lang.reflect.InvocationTargetException: null
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at org.apache.ibatis.logging.LogFactory.setImplementation(LogFactory.java:133)
	... 75 common frames omitted
Caused by: java.lang.NoClassDefFoundError: org/apache/logging/log4j/LogManager
	at org.apache.ibatis.logging.log4j2.Log4j2Impl.<init>(Log4j2Impl.java:31)
	... 80 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.apache.logging.log4j.LogManager
	at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 81 common frames omitted

logback.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<configuration  scan="true" scanPeriod="60 seconds" debug="false">
<!--     <contextName>logback</contextName> -->
    <property name="log.path" value="../as300_web_logs/service_monitor/service_monitor" />
    <property name="log.level" value="info" />
    <!--输出到控制台-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
       <!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>-->
        <encoder>
        <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> 
            <!-- <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern> -->
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--输出到文件-->
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 按照日期和文件大小创建日志文件,每天一个文件,如果超过20M创建新的文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${log.path}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>20MB</maxFileSize>
			<maxHistory>7</maxHistory> <!-- 最多保留7天的日志文件 -->
			<totalSizeCap>20GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- name是打印sql信息的包名,即会打印此包下sql -->
    <logger name="com.focus.application" level="debug" />
        <!-- mybatis日志打印-->
    <logger name="org.apache.ibatis" level="DEBUG" />
    <logger name="java.sql" level="DEBUG" />

    <root level="info,debug">
        <appender-ref ref="console" />
        <appender-ref ref="file" />
    </root>
</configuration>

第三步:看一下mybatis源代码BaseExceutor底层原因

  protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog);
    } else {
      return connection;
    }
  }

假如:设定了STDOUT_LOGGING,实现类是StdOutImpl.java

  public boolean isDebugEnabled() {
    return true;
  }

 debug就开启了,log就可以打印sql了,logback.xml配置:

    <logger name="org.apache.ibatis" level="DEBUG">
		<appender-ref ref="STDOUT"/>
	</logger>
	
	<logger name="java.sql" level="debug">
	    <appender-ref ref="STDOUT"/>
	</logger> 

百转千回~