springboot打出mybatis日志
springboot默认的日志框架是logback,只需要添加logback的配置即可打印;但若是换成了log4j2,则需要添加如下配置;如何将springboot日志换成log4j2,参考springboot换log4j2写日志本篇文章;使用log4j2打印mybatis日志,只需要下面两步即可;
添加gradle引用如下:
compile("org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.4") compile("mysql:mysql-connector-java")
1,换完后,只需要在resources下面添加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="logImpl" value="LOG4J2"/> </settings> </configuration>
2,在application.xml中添加如下配置:
mybatis: configLocation: classpath:mybatis-config.xml
接下来从mybatis源码分析一下为什么需要加该配置,其中涉及到的一个很重要的类是org.apache.ibatis.logging.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 static void tryImplementation(Runnable runnable) { if (logConstructor == null) { try { runnable.run(); } catch (Throwable t) { // ignore } } } 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); } 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); } } }
如上所示,tryImplementation方法中,会判断当logConstructor == null才会执行,当表态构造中执行完useSlf4jLogging后,静态构造里面其它方法都不会再执行了,logConstructor直接就给赋值org.apache.ibatis.logging.slf4j.Slf4jImpl.class为该类的实例了。所以必须添加配置,才能使logConstructor的值发生改变
当添加完配置时:org.apache.ibatis.session.Configuration中setLogImpl方法会重新设置上面logConstructor的值,代码如下:
public void setLogImpl(Class<? extends Log> logImpl) { if (logImpl != null) { this.logImpl = logImpl; LogFactory.useCustomLogging(this.logImpl); } }
那么配置为什么是<setting name="logImpl" value="LOG4J2"/>呢,org.apache.ibatis.builder.xml.XMLConfigBuilder该类中loadCustomLogImpl给出了答案,代码如下:
private void loadCustomLogImpl(Properties props) { Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl")); configuration.setLogImpl(logImpl); }
从上面代码可以看出,配置中name值必须为logImpl,否则读取不到值,而loadCustomLogImpl方法的参数来自该类的parseConfiguration方法,代码如下:
private void parseConfiguration(XNode root) { try { // issue #117 read properties first propertiesElement(root.evalNode("properties")); Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); loadCustomLogImpl(settings); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
从第5,6行就可以看出来,配置是从settings中读取出来的
到于为什么值是LOG4J2,从org.apache.ibatis.session.Configuration的构造函数中就可以得出,如下代码所示:
public Configuration() { typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class); typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class); typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class); typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class); typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class); typeAliasRegistry.registerAlias("FIFO", FifoCache.class); typeAliasRegistry.registerAlias("LRU", LruCache.class); typeAliasRegistry.registerAlias("SOFT", SoftCache.class); typeAliasRegistry.registerAlias("WEAK", WeakCache.class); typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class); typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class); typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class); 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); typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class); typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class); languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class); languageRegistry.register(RawLanguageDriver.class); }
那么为什么要在application.xml中添加配置呢?springboot在启动时,会读取mybatis配置(下面即是配置类),若没有配置,则会用使用上面源码逻辑中所写的logback打印日志,代码如下所示:
配置中configLocation字段即是在application.xml中配置的mybatis xml config的路径,配置类为:org.mybatis.spring.boot.autoconfigure.MybatisProperties
@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX) public class MybatisProperties { public static final String MYBATIS_PREFIX = "mybatis"; private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); /** * Location of MyBatis xml config file. */ private String configLocation; /** * Locations of MyBatis mapper files. */ private String[] mapperLocations; }
总结:mybatis使用log4j2打印日志,需要添加配置mybatis-config.xml,在settings中指定 logImpl = LOG4J2;并且在application.xml中添加配置引用即可