springboot+mybatis解决日志不打印sql的问题
目录
第一步:首先处理pom.xml中的依赖关系,将日志的实现切换为logback;
第三步:看一下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是不好用的。这是官方文档的描述,见下图:
依次将以上实现都试了试:
COMMONS_LOGGING |JDK_LOGGING(默认配置)或者不加此setting配置(相当于默认加了JDK_LOGGING配置):正确打印预期日志,符合要求
STDOUT_LOGGING :没有问题,只是打印日志的格式不合心意,明明配置了打印的格式
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>
百转千回~