最近有个需求,记录框架spring+mybatis的项目的慢sql,想到了log4jdbc框架,log4jdbc只是对传统jdbc的一层封装,然后打印出sql执行日志和执行时间:
先简单介绍一下log4jdbc的使用:
第一步:引用log4jdbc的gradle配置,如果是maven的自己转换:
compile 'com.googlecode.log4jdbc:log4jdbc:1.2'
第二步:修改jdbc的driver和url:
mysql: datasource: driverClassName: net.sf.log4jdbc.DriverSpy url: jdbc:log4jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=UTC
就是这么简单,修改了这2个地方,就可以直接使用了,现在只要操作了数据库,日志便会打印出sql和sql的执行时间,以下面的这种格式:
然而具体的执行过程是怎么样的呢,我们有必要先阅读一下log4jdbc的源码,log4jdbc的源码包并不大,只有十几个类:
DriverSpy是对java.sql.Driver接口的实现,ConnectionSpy是对java.sql.Connection接口的实现,先来看看Class.forName(“net.sf.log4jdbc.DriverSpy”)时到底干了什么,先来看看DriverSpy的static静态代码块:
static { //log4jdbc初始化开始 log.debug("... log4jdbc initializing ..."); //从classpath下读取配置文件 InputStream propStream = DriverSpy.class.getResourceAsStream("/log4jdbc.properties"); Properties props = new Properties(System.getProperties()); if (propStream != null) { try { props.load(propStream); } catch (IOException e) { log.debug("ERROR! io exception loading " + "log4jdbc.properties from classpath: " + e.getMessage()); } finally { try { propStream.close(); } catch (IOException e) { log.debug("ERROR! io exception closing property file stream: " + e.getMessage()); } } log.debug(" log4jdbc.properties loaded from classpath"); } else { log.debug(" log4jdbc.properties not found on classpath"); } // look for additional driver specified in properties //加载dubug模式的前缀 DebugStackPrefix = getStringOption(props, "log4jdbc.debug.stack.prefix"); TraceFromApplication = DebugStackPrefix != null; //当SqlTimingWarnThresholdEnabled 为true时,SqlTimingWarnThresholdMsec 才有效,当sql执行时间大于 //SqlTimingWarnThresholdMsec 时间时,才打印warn日志 Long thresh = getLongOption(props, "log4jdbc.sqltiming.warn.threshold"); SqlTimingWarnThresholdEnabled = (thresh != null); if (SqlTimingWarnThresholdEnabled) { SqlTimingWarnThresholdMsec = thresh.longValue(); } thresh = getLongOption(props, "log4jdbc.sqltiming.error.threshold"); SqlTimingErrorThresholdEnabled = (thresh != null); if (SqlTimingErrorThresholdEnabled) { SqlTimingErrorThresholdMsec = thresh.longValue(); } DumpBooleanAsTrueFalse = getBooleanOption(props, "log4jdbc.dump.booleanastruefalse",false); DumpSqlMaxLineLength = getLongOption(props, "log4jdbc.dump.sql.maxlinelength", 90L).intValue(); DumpFullDebugStackTrace = getBooleanOption(props, "log4jdbc.dump.fulldebugstacktrace",false); StatementUsageWarn = getBooleanOption(props, "log4jdbc.statement.warn",false); //默认的打印所有类型的日志,也可以自己在配置文件中配置只打印某一种类型的日志,比如“select” DumpSqlSelect = getBooleanOption(props, "log4jdbc.dump.sql.select",true); DumpSqlInsert = getBooleanOption(props, "log4jdbc.dump.sql.insert",true); DumpSqlUpdate = getBooleanOption(props, "log4jdbc.dump.sql.update",true); DumpSqlDelete = getBooleanOption(props, "log4jdbc.dump.sql.delete",true); DumpSqlCreate = getBooleanOption(props, "log4jdbc.dump.sql.create",true); DumpSqlFilteringOn = !(DumpSqlSelect && DumpSqlInsert && DumpSqlUpdate && DumpSqlDelete && DumpSqlCreate); DumpSqlAddSemicolon = getBooleanOption(props, "log4jdbc.dump.sql.addsemicolon", false); //是否加载所有有名气的数据库驱动,默认是加载 AutoLoadPopularDrivers = getBooleanOption(props, "log4jdbc.auto.load.popular.drivers", true); TrimSql = getBooleanOption(props, "log4jdbc.trim.sql", true); TrimExtraBlankLinesInSql = getBooleanOption(props, "log4jdbc.trim.sql.extrablanklines", true); SuppressGetGeneratedKeysException = getBooleanOption(props, "log4jdbc.suppress.generated.keys.exception", false); // The Set of drivers that the log4jdbc driver will preload at instantiation // time. The driver can spy on any driver type, it's just a little bit // easier to configure log4jdbc if it's one of these types! Set subDrivers = new TreeSet(); if (AutoLoadPopularDrivers) { subDrivers.add("oracle.jdbc.driver.OracleDriver"); subDrivers.add(