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