http://www.mybatis.org/mybatis-3/zh/logging.htmlhtml
咱们从Mybatis配置文件中的日志配置开始,来看看它究竟是怎么实现的 sql
<configuration> <settings> <setting name="logImpl" value="NO_LOGGING"/> </settings> </configuration>
@Test public void shouldReadLogImplFromSettings() throws Exception { Reader reader = Resources.getResourceAsReader("org/apache/ibatis/logging/mybatis-config.xml"); new SqlSessionFactoryBuilder().build(reader); reader.close(); Log log = LogFactory.getLog(Object.class); log.debug("Debug message."); assertEquals(log.getClass().getName(), NoLoggingImpl.class.getName()); }
configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
在初始化配置过程当中,Builder还初始化了如下对象中的数据:数据库
public BaseBuilder(Configuration configuration) { this.configuration = configuration; this.typeAliasRegistry = this.configuration.getTypeAliasRegistry(); this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); }
// 注册经常使用的日志类 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); // 指定具体的日志实现类 public void setLogImpl(Class<?> logImpl) { if (logImpl != null) { this.logImpl = (Class<? extends Log>) logImpl; LogFactory.useCustomLogging(this.logImpl); } } //LogFactory中useCustomLogging的方法: public static synchronized void useCustomLogging(Class<? extends Log> clazz) { setImplementation(clazz); }
private static Constructor<? extends Log> logConstructor; //设置实现类的方法: private static void setImplementation(Class<? extends Log> implClass) { try { Constructor<? extends Log> candidate = implClass.getConstructor(new Class[] { String.class }); Log log = candidate.newInstance(new Object[] { LogFactory.class.getName() }); log.debug("Logging initialized using '" + implClass + "' adapter."); //设置logConstructor,一旦设上,代表找到相应的log的jar包了,那后面别的log就不找了。 logConstructor = candidate; } catch (Throwable t) { throw new LogException("Error setting Log implementation. Cause: " + t, t); } } //根据传入的类名来构建Log public static Log getLog(String logger) { try { //构造函数,参数必须是一个,为String型,指明logger的名称 return logConstructor.newInstance(new Object[] { logger }); } catch (Throwable t) { throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t); } }
public class Log4jImpl implements Log
Mybatis在获取执行一条SQL语句的时候,对Connection,Statement,ResultSet,PreparedStatement做了代理,经过代理实现实现了日志打印。 apache
protected Connection getConnection(Log statementLog) throws SQLException { Connection connection = transaction.getConnection(); if (statementLog.isDebugEnabled()) { //若是须要打印Connection的日志,返回一个ConnectionLogger(代理模式) return ConnectionLogger.newInstance(connection, statementLog, queryStack); } else { return connection; } }
//log4j为例 log4j.logger.org.mybatis.example=TRACE
经过Excutor接口发现,执行SQL的MappedStatement都持有一个Log接口,这个接口的具体设置是在其Builder中建立,也就是说只有在Builder的时候设置才生效。而在获取Log的时候直接返回了设置的statementLog,而且MappedStatement的持有的Log为private,也没有提供公共的设置方法,所以,只能经过修改源代码的方式来设置Logsession
public final class MappedStatement { //持有的Log接口 private Log statementLog; ...... public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) { mappedStatement.configuration = configuration; . .... String logId = id; if (configuration.getLogPrefix() != null) { logId = configuration.getLogPrefix() + id; } mappedStatement.statementLog = LogFactory.getLog(logId); mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance(); } //获取Log时调用: public Log getStatementLog() { return statementLog; } //修改成经过LogFactory获取 public Log getStatementLog() { String logId = this.getId(); if (configuration.getLogPrefix() != null) { logId = configuration.getLogPrefix() + id; } return LogFactory.getLog(logId); } }
动态修改Log打印实现:mybatis
@RequestMapping("/log/{type}") public Result log(@PathVariable("type")String type) { if("on".equals(type)){ LogFactory.useStdOutLogging(); }else { LogFactory.useSlf4jLogging(); } return new Result().success(); }