diff --git a/pom.xml b/pom.xml index c85f241923..688d2c145b 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.alibaba druid - 0.2.19-SNAPSHOT + 0.2.19 jar druid diff --git a/src/main/java/com/alibaba/druid/VERSION.java b/src/main/java/com/alibaba/druid/VERSION.java index f9dfab2ea6..4e81edcb72 100644 --- a/src/main/java/com/alibaba/druid/VERSION.java +++ b/src/main/java/com/alibaba/druid/VERSION.java @@ -19,7 +19,7 @@ public final class VERSION { public final static int MajorVersion = 0; public final static int MinorVersion = 2; - public final static int RevisionVersion = 18; + public final static int RevisionVersion = 19; public static String getVersionNumber() { return VERSION.MajorVersion + "." + VERSION.MinorVersion + "." + VERSION.RevisionVersion; diff --git a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java index 6fc3528c78..6adb7d53bb 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidAbstractDataSource.java @@ -210,7 +210,7 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements private ObjectName objectName; - private final AtomicLong executeCount = new AtomicLong(); + protected final AtomicLong executeCount = new AtomicLong(); protected volatile Throwable createError; protected volatile Throwable lastError; @@ -231,7 +231,10 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements private Boolean useUnfairLock = null; - private boolean useLocalSessionState = true; + private boolean useLocalSessionState = true; + + protected long timeBetweenLogStatsMillis; + protected DruidDataSourceStatLogger statLogger = new DruidDataSourceStatLoggerImpl(); public DruidAbstractDataSource(boolean lockFair){ lock = new ReentrantLock(lockFair); @@ -239,15 +242,42 @@ public abstract class DruidAbstractDataSource extends WrapperAdapter implements notEmpty = lock.newCondition(); empty = lock.newCondition(); } - + public boolean isUseLocalSessionState() { return useLocalSessionState; } - + public void setUseLocalSessionState(boolean useLocalSessionState) { this.useLocalSessionState = useLocalSessionState; } + public DruidDataSourceStatLogger getStatLogger() { + return statLogger; + } + + public void setStatLoggerClassName(String className) { + Class clazz; + try { + clazz = Class.forName(className); + DruidDataSourceStatLogger statLogger = (DruidDataSourceStatLogger) clazz.newInstance(); + this.setStatLogger(statLogger); + } catch (Exception e) { + throw new IllegalArgumentException(className, e); + } + } + + public void setStatLogger(DruidDataSourceStatLogger statLogger) { + this.statLogger = statLogger; + } + + public long getTimeBetweenLogStatsMillis() { + return timeBetweenLogStatsMillis; + } + + public void setTimeBetweenLogStatsMillis(long timeBetweenLogStatsMillis) { + this.timeBetweenLogStatsMillis = timeBetweenLogStatsMillis; + } + public boolean isOracle() { return isOracle; } diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java index 6ddbc2c4f1..1db4b9488d 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSource.java @@ -82,6 +82,7 @@ import com.alibaba.druid.util.JdbcUtils; import com.alibaba.druid.util.StringUtils; import com.alibaba.druid.wall.WallFilter; +; /** * @author ljw * @author wenshao @@ -119,6 +120,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat // threads private CreateConnectionThread createConnectionThread; private DestroyConnectionThread destoryConnectionThread; + private LogStatsThread logStatsThread; private final CountDownLatch initedLatch = new CountDownLatch(2); @@ -131,7 +133,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat private volatile boolean closed = false; private long closeTimeMillis = -1L; - private JdbcDataSourceStat dataSourceStat; + protected JdbcDataSourceStat dataSourceStat; private boolean useGloalDataSourceStat = false; @@ -175,14 +177,23 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat { String property = System.getProperty("druid.filters"); - if (property == null || property.length() == 0) { - return; + if (property != null && property.length() > 0) { + try { + this.setFilters(property); + } catch (SQLException e) { + LOG.error("setFilters error", e); + } } - - try { - this.setFilters(property); - } catch (SQLException e) { - LOG.error("setFilters error", e); + } + { + String property = System.getProperty("druid.timeBetweenLogStatsMillis"); + if (property != null && property.length() > 0) { + try { + long value = Long.parseLong(property); + this.setTimeBetweenLogStatsMillis(value); + } catch (NumberFormatException e) { + LOG.error("illegal property 'timeBetweenLogStatsMillis'", e); + } } } } @@ -436,6 +447,10 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat + maxActive); } + if (timeBetweenLogStatsMillis > 0 && useGloalDataSourceStat) { + throw new IllegalArgumentException("timeBetweenLogStatsMillis not support useGloalDataSourceStat=true"); + } + if (this.driverClass != null) { this.driverClass = driverClass.trim(); } @@ -492,7 +507,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat SQLException connectError = null; try { - // 初始化连接 + // init connections for (int i = 0, size = getInitialSize(); i < size; ++i) { Connection conn = createPhysicalConnection(); DruidConnectionHolder holder = new DruidConnectionHolder(this, conn); @@ -508,6 +523,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat connectError = ex; } + createAndLogThread(); createAndStartCreatorThread(); createAndStartDestroyThread(); @@ -535,6 +551,16 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } } + private void createAndLogThread() { + if (this.timeBetweenLogStatsMillis <= 0) { + return; + } + + String threadName = "Druid-ConnectionPool-Log-" + System.identityHashCode(this); + logStatsThread = new LogStatsThread(threadName); + logStatsThread.start(); + } + private void createAndStartDestroyThread() { String threadName = "Druid-ConnectionPool-Destory-" + System.identityHashCode(this); destoryConnectionThread = new DestroyConnectionThread(threadName); @@ -1047,7 +1073,7 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat recycleErrorCount.incrementAndGet(); } } - + public long getRecycleErrorCount() { return recycleErrorCount.get(); } @@ -1078,6 +1104,10 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat return; } + if (logStatsThread != null) { + logStatsThread.interrupt(); + } + if (createConnectionThread != null) { createConnectionThread.interrupt(); } @@ -1236,13 +1266,13 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat @Override public Connection getConnection(String username, String password) throws SQLException { if (!StringUtils.equals(username, this.username)) { - throw new UnsupportedOperationException("Not supported by DruidDataSource"); + throw new UnsupportedOperationException("Not supported by DruidDataSource"); } - + if (!StringUtils.equals(password, this.password)) { - throw new UnsupportedOperationException("Not supported by DruidDataSource"); + throw new UnsupportedOperationException("Not supported by DruidDataSource"); } - + return getConnection(); } @@ -1311,6 +1341,103 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } } + public void logStats() { + final DruidDataSourceStatLogger statLogger = this.statLogger; + if (statLogger == null) { + return; + } + + DruidDataSourceStatValue statValue = getStatValueAndReset(); + + statLogger.log(statValue); + } + + public DruidDataSourceStatValue getStatValueAndReset() { + DruidDataSourceStatValue value = new DruidDataSourceStatValue(); + + lock.lock(); + try { + value.poolingCount = this.poolingCount; + value.poolingPeak = this.poolingPeak; + value.poolingPeakTime = this.poolingPeakTime; + + value.activeCount = this.activeCount; + value.activePeak = this.activePeak; + value.activePeakTime = this.activePeakTime; + + value.connectCount = this.connectCount; + value.closeCount = this.closeCount; + value.waitThreadCount = lock.getWaitQueueLength(notEmpty); + value.notEmptyWaitCount = this.notEmptyWaitCount; + value.notEmptyWaitNanos = this.notEmptyWaitNanos; + + // reset + this.poolingPeak = 0; + this.poolingPeakTime = 0; + this.activePeak = 0; + this.activePeakTime = 0; + this.connectCount = 0; + this.closeCount = 0; + + this.notEmptyWaitCount = 0; + this.notEmptyWaitNanos = 0; + } finally { + lock.unlock(); + } + + value.name = this.getName(); + value.dbType = this.getDbType(); + value.driverClassName = this.getDriverClassName(); + + value.url = this.getUrl(); + value.userName = this.getUsername(); + value.filterClassNames = this.getFilterClassNames(); + + value.initialSize = this.getInitialSize(); + value.minIdle = this.getMinIdle(); + value.maxActive = this.getMaxActive(); + + value.queryTimeout = this.getQueryTimeout(); + value.transactionQueryTimeout = this.getTransactionQueryTimeout(); + value.loginTimeout = this.getLoginTimeout(); + value.validConnectionCheckerClassName = this.getValidConnectionCheckerClassName(); + value.exceptionSorterClassName = this.getExceptionSorterClassName(); + + value.testOnBorrow = this.isTestOnBorrow(); + value.testOnReturn = this.isTestOnReturn(); + value.testWhileIdle = this.isTestWhileIdle(); + + value.defaultAutoCommit = this.isDefaultAutoCommit(); + value.defaultReadOnly = this.isDefaultAutoCommit(); + value.defaultTransactionIsolation = this.getDefaultTransactionIsolation(); + + value.logicConnectErrorCount = connectErrorCount.getAndSet(0); + + value.physicalConnectCount = createCount.getAndSet(0); + value.physicalCloseCount = destroyCount.getAndSet(0); + value.physicalConnectErrorCount = createErrorCount.getAndSet(0); + + value.executeCount = this.executeCount.getAndSet(0); + value.errorCount = errorCount.getAndSet(0); + value.commitCount = commitCount.getAndSet(0); + value.rollbackCount = rollbackCount.getAndSet(0); + + value.pstmtCacheHitCount = cachedPreparedStatementHitCount.getAndSet(0); + value.pstmtCacheMissCount = cachedPreparedStatementMissCount.getAndSet(0); + + value.startTransactionCount = startTransactionCount.getAndSet(0); + value.transactionHistogram = this.getTransactionHistogram().toArrayAndReset(); + + value.connectionHoldTimeHistogram = this.getDataSourceStat().getConnectionHoldHistogram().toArrayAndReset(); + value.removeAbandoned = this.isRemoveAbandoned(); + value.clobOpenCount = this.getDataSourceStat().getClobOpenCountAndReset(); + value.blobOpenCount = this.getDataSourceStat().getBlobOpenCountAndReset(); + + value.sqlList = this.getDataSourceStat().getSqlStatMapAndReset(); + + return value; + } + public long getRemoveAbandonedCount() { return removeAbandonedCount; } @@ -1454,6 +1581,30 @@ public class DruidDataSource extends DruidAbstractDataSource implements DruidDat } + public class LogStatsThread extends Thread { + + public LogStatsThread(String name){ + super(name); + this.setDaemon(true); + } + + public void run() { + try { + for (;;) { + try { + logStats(); + } catch (Exception e) { + LOG.error("logStats error", e); + } + + Thread.sleep(timeBetweenLogStatsMillis); + } + } catch (InterruptedException e) { + // skip + } + } + } + public int removeAbandoned() { int removeCount = 0; diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSourceMBean.java b/src/main/java/com/alibaba/druid/pool/DruidDataSourceMBean.java index 5e2d5e364c..270de4d309 100644 --- a/src/main/java/com/alibaba/druid/pool/DruidDataSourceMBean.java +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSourceMBean.java @@ -72,4 +72,10 @@ public interface DruidDataSourceMBean extends DruidAbstractDataSourceMBean { void clearStatementCache() throws SQLException; long getDiscardCount(); + + void setStatLoggerClassName(String className); + + long getTimeBetweenLogStatsMillis(); + + void setTimeBetweenLogStatsMillis(long timeBetweenLogStatsMillis); } diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSourceStatLogger.java b/src/main/java/com/alibaba/druid/pool/DruidDataSourceStatLogger.java new file mode 100644 index 0000000000..80b46da108 --- /dev/null +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSourceStatLogger.java @@ -0,0 +1,21 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.pool; + +public interface DruidDataSourceStatLogger { + + void log(DruidDataSourceStatValue statValue); +} diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSourceStatLoggerImpl.java b/src/main/java/com/alibaba/druid/pool/DruidDataSourceStatLoggerImpl.java new file mode 100644 index 0000000000..f60a6417bc --- /dev/null +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSourceStatLoggerImpl.java @@ -0,0 +1,168 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.pool; + +import static com.alibaba.druid.util.JdbcSqlStatUtils.rtrim; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.alibaba.druid.stat.JdbcSqlStatValue; +import com.alibaba.druid.support.json.JSONUtils; +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; + +public class DruidDataSourceStatLoggerImpl implements DruidDataSourceStatLogger { + + private static Log LOG = LogFactory.getLog(DruidDataSourceStatLoggerImpl.class); + + @Override + public void log(DruidDataSourceStatValue statValue) { + if (LOG.isInfoEnabled()) { + Map map = new LinkedHashMap(); + + map.put("url", statValue.url); + map.put("dbType", statValue.dbType); + map.put("name", statValue.name); + map.put("activeCount", statValue.activeCount); + + if (statValue.activePeak > 0) { + map.put("activePeak", statValue.activePeak); + map.put("activePeakTime", statValue.getActivePeakTime()); + } + map.put("poolingCount", statValue.poolingCount); + if (statValue.poolingPeak > 0) { + map.put("poolingPeak", statValue.poolingPeak); + map.put("poolingPeakTime", statValue.getPoolingPeakTime()); + } + map.put("connectCount", statValue.connectCount); + map.put("closeCount", statValue.closeCount); + + if (statValue.waitThreadCount > 0) { + map.put("waitThreadCount", statValue.waitThreadCount); + } + if (statValue.notEmptyWaitCount > 0) { + map.put("notEmptyWaitCount", statValue.notEmptyWaitCount); + } + if (statValue.getNotEmptyWaitMillis() > 0) { + map.put("notEmptyWaitMillis", statValue.getNotEmptyWaitMillis()); + } + if (statValue.logicConnectErrorCount > 0) { + map.put("logicConnectErrorCount", statValue.logicConnectErrorCount); + } + if (statValue.physicalConnectCount > 0) { + map.put("physicalConnectCount", statValue.physicalConnectCount); + } + if (statValue.physicalCloseCount > 0) { + map.put("physicalCloseCount", statValue.physicalCloseCount); + } + if (statValue.physicalConnectErrorCount > 0) { + map.put("physicalConnectErrorCount", statValue.physicalConnectErrorCount); + } + if (statValue.executeCount > 0) { + map.put("executeCount", statValue.executeCount); + } + if (statValue.errorCount > 0) { + map.put("errorCount", statValue.errorCount); + } + if (statValue.commitCount > 0) { + map.put("commitCount", statValue.commitCount); + } + if (statValue.rollbackCount > 0) { + map.put("rollbackCount", statValue.rollbackCount); + } + if (statValue.pstmtCacheHitCount > 0) { + map.put("pstmtCacheHitCount", statValue.pstmtCacheHitCount); + } + if (statValue.pstmtCacheMissCount > 0) { + map.put("pstmtCacheMissCount", statValue.pstmtCacheMissCount); + } + if (statValue.startTransactionCount > 0) { + map.put("startTransactionCount", statValue.startTransactionCount); + map.put("transactionHistogram", rtrim(statValue.transactionHistogram)); + } + if (statValue.connectCount > 0) { + map.put("connectionHoldTimeHistogram", rtrim(statValue.connectionHoldTimeHistogram)); + } + if (statValue.clobOpenCount > 0) { + map.put("clobOpenCount", statValue.clobOpenCount); + } + if (statValue.blobOpenCount > 0) { + map.put("blobOpenCount", statValue.blobOpenCount); + } + + ArrayList> sqlList = new ArrayList>(); + if (statValue.sqlList.size() > 0) { + for (JdbcSqlStatValue sqlStat : statValue.sqlList) { + Map sqlStatMap = new LinkedHashMap(); + sqlStatMap.put("sql", sqlStat.sql); + + if (sqlStat.getExecuteCount() > 0) { + sqlStatMap.put("executeCount", sqlStat.getExecuteCount()); + sqlStatMap.put("executeMillisMax", sqlStat.getExecuteMillisMax()); + sqlStatMap.put("executeMillisTotal", sqlStat.getExecuteMillisTotal()); + + sqlStatMap.put("executeHistogram", rtrim(sqlStat.getExecuteHistogram())); + sqlStatMap.put("executeAndResultHoldHistogram", + rtrim(sqlStat.getExecuteAndResultHoldHistogram())); + } + + if (sqlStat.executeErrorCount > 0) { + sqlStatMap.put("executeErrorCount", sqlStat.executeErrorCount); + } + if (sqlStat.runningCount > 0) { + sqlStatMap.put("runningCount", sqlStat.runningCount); + } + sqlStatMap.put("concurrentMax", sqlStat.concurrentMax); + + if (sqlStat.fetchRowCount > 0) { + sqlStatMap.put("fetchRowCount", sqlStat.fetchRowCount); + sqlStatMap.put("fetchRowCount", sqlStat.fetchRowCountMax); + sqlStatMap.put("fetchRowHistogram", rtrim(sqlStat.getFetchRowHistogram())); + } + + if (sqlStat.updateCount > 0) { + sqlStatMap.put("updateCount", sqlStat.updateCount); + sqlStatMap.put("updateCountMax", sqlStat.updateCountMax); + sqlStatMap.put("updateHistogram", rtrim(sqlStat.getUpdateHistogram())); + } + + if (sqlStat.inTransactionCount > 0) { + sqlStatMap.put("inTransactionCount", sqlStat.inTransactionCount); + } + + if (sqlStat.clobOpenCount > 0) { + sqlStatMap.put("clobOpenCount", sqlStat.clobOpenCount); + } + + if (sqlStat.blobOpenCount > 0) { + sqlStatMap.put("blobOpenCount", sqlStat.blobOpenCount); + } + + sqlList.add(sqlStatMap); + } + + map.put("sqlList", sqlList); + } + + String text = JSONUtils.toJSONString(map); + + LOG.info(text); + } + } + +} diff --git a/src/main/java/com/alibaba/druid/pool/DruidDataSourceStatValue.java b/src/main/java/com/alibaba/druid/pool/DruidDataSourceStatValue.java new file mode 100644 index 0000000000..5f7f717962 --- /dev/null +++ b/src/main/java/com/alibaba/druid/pool/DruidDataSourceStatValue.java @@ -0,0 +1,100 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.pool; + +import java.util.Date; +import java.util.List; + +import com.alibaba.druid.stat.JdbcSqlStatValue; + +public class DruidDataSourceStatValue { + + public String name; + public String dbType; + public String driverClassName; + public String url; + public String userName; + public List filterClassNames; + public boolean removeAbandoned; + + public int initialSize; + public int minIdle; + public int maxActive; + public int queryTimeout; + public int transactionQueryTimeout; + public int loginTimeout; + public String validConnectionCheckerClassName; + public String exceptionSorterClassName; + public boolean testOnBorrow; + public boolean testOnReturn; + public boolean testWhileIdle; + public boolean defaultAutoCommit; + public boolean defaultReadOnly; + public Integer defaultTransactionIsolation; + + public int activeCount; + public int activePeak; + public long activePeakTime; + + public int poolingCount; + public int poolingPeak; + public long poolingPeakTime; + + public long connectCount; + public long closeCount; + public long waitThreadCount; + public long notEmptyWaitCount; + public long notEmptyWaitNanos; + + public long logicConnectErrorCount; + public long physicalConnectCount; + public long physicalCloseCount; + public long physicalConnectErrorCount; + public long executeCount; + public long errorCount; + public long commitCount; + public long rollbackCount; + public long pstmtCacheHitCount; + public long pstmtCacheMissCount; + public long startTransactionCount; + public long[] transactionHistogram; + public long[] connectionHoldTimeHistogram; + + public long clobOpenCount; + public long blobOpenCount; + + public List sqlList; + + public Date getPoolingPeakTime() { + if (poolingPeakTime <= 0) { + return null; + } + + return new Date(poolingPeakTime); + } + + public Date getActivePeakTime() { + if (activePeakTime <= 0) { + return null; + } + + return new Date(activePeakTime); + } + + public long getNotEmptyWaitMillis() { + return notEmptyWaitNanos / (1000 * 1000); + } +} diff --git a/src/main/java/com/alibaba/druid/stat/JdbcDataSourceStat.java b/src/main/java/com/alibaba/druid/stat/JdbcDataSourceStat.java index 661b06dea1..b34d71c410 100644 --- a/src/main/java/com/alibaba/druid/stat/JdbcDataSourceStat.java +++ b/src/main/java/com/alibaba/druid/stat/JdbcDataSourceStat.java @@ -15,9 +15,10 @@ */ package com.alibaba.druid.stat; -import java.util.HashMap; +import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; @@ -257,7 +258,7 @@ public class JdbcDataSourceStat implements JdbcDataSourceStatMBean { } public Map getSqlStatMap() { - Map map = new HashMap(); + Map map = new LinkedHashMap(sqlStatMap.size()); lock.readLock().lock(); try { map.putAll(sqlStatMap); @@ -266,6 +267,34 @@ public class JdbcDataSourceStat implements JdbcDataSourceStatMBean { } return map; } + + public List getSqlStatMapAndReset() { + List stats = new ArrayList(sqlStatMap.size()); + lock.writeLock().lock(); + try { + Iterator> iter = sqlStatMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + JdbcSqlStat stat = entry.getValue(); + if (stat.getExecuteCount() == 0 && stat.getRunningCount() == 0) { + stat.setRemoved(true); + iter.remove(); + } else { + stats.add(entry.getValue()); + } + } + } finally { + lock.writeLock().unlock(); + } + + List values = new ArrayList(stats.size()); + for (int i = 0; i < stats.size(); ++i) { + JdbcSqlStat stat = stats.get(i); + JdbcSqlStatValue value = stat.getValueAndReset(); + values.add(value); + } + return values; + } public JdbcSqlStat getSqlStat(String sql) { lock.readLock().lock(); @@ -346,6 +375,10 @@ public class JdbcDataSourceStat implements JdbcDataSourceStatMBean { public long getClobOpenCount() { return clobOpenCount.get(); } + + public long getClobOpenCountAndReset() { + return clobOpenCount.getAndSet(0); + } public void incrementClobOpenCount() { clobOpenCount.incrementAndGet(); @@ -354,6 +387,10 @@ public class JdbcDataSourceStat implements JdbcDataSourceStatMBean { public long getBlobOpenCount() { return blobOpenCount.get(); } + + public long getBlobOpenCountAndReset() { + return blobOpenCount.getAndSet(0); + } public void incrementBlobOpenCount() { blobOpenCount.incrementAndGet(); diff --git a/src/main/java/com/alibaba/druid/stat/JdbcSqlStat.java b/src/main/java/com/alibaba/druid/stat/JdbcSqlStat.java index 33de13886a..74a9dc0fbf 100644 --- a/src/main/java/com/alibaba/druid/stat/JdbcSqlStat.java +++ b/src/main/java/com/alibaba/druid/stat/JdbcSqlStat.java @@ -323,7 +323,6 @@ public final class JdbcSqlStat implements JdbcSqlStatMBean { executeSpanNanoTotalUpdater.set(this, 0); executeSpanNanoMaxUpdater.set(this, 0); executeNanoSpanMaxOccurTime = 0; - runningCountUpdater.set(this, 0); concurrentMaxUpdater.set(this, 0); executeErrorCountUpdater.set(this, 0); @@ -380,6 +379,88 @@ public final class JdbcSqlStat implements JdbcSqlStatMBean { readerOpenCountUpdater.set(this, 0); } + public JdbcSqlStatValue getValueAndReset() { + JdbcSqlStatValue val = new JdbcSqlStatValue(); + + val.sql = sql; + val.executeLastStartTime = executeLastStartTime; + executeLastStartTime = 0; + + val.executeBatchSizeTotal = executeBatchSizeTotalUpdater.getAndSet(this, 0); + val.executeBatchSizeMax = executeBatchSizeMaxUpdater.getAndSet(this, 0); + + val.executeSuccessCount = executeSuccessCountUpdater.getAndSet(this, 0); + val.executeSpanNanoTotal = executeSpanNanoTotalUpdater.getAndSet(this, 0); + val.executeSpanNanoMax = executeSpanNanoMaxUpdater.getAndSet(this, 0); + val.executeNanoSpanMaxOccurTime = executeNanoSpanMaxOccurTime; + executeNanoSpanMaxOccurTime = 0; + + val.runningCount = this.runningCount; + + val.concurrentMax = concurrentMaxUpdater.getAndSet(this, 0); + + val.executeErrorCount = executeErrorCountUpdater.getAndSet(this, 0); + + val.executeErrorLast = executeErrorLast; + executeErrorLast = null; + + val.executeErrorLastTime = executeErrorLastTime; + executeErrorLastTime = 0; + + val.updateCount = updateCountUpdater.getAndSet(this, 0); + val.updateCountMax = updateCountMaxUpdater.getAndSet(this, 0); + val.fetchRowCount = fetchRowCountUpdater.getAndSet(this, 0); + val.fetchRowCountMax = fetchRowCountMaxUpdater.getAndSet(this, 0); + + val.histogram_0_1 = histogram_0_1_Updater.getAndSet(this, 0); + val.histogram_1_10 = histogram_1_10_Updater.getAndSet(this, 0); + val.histogram_10_100 = histogram_10_100_Updater.getAndSet(this, 0); + val.histogram_100_1000 = histogram_100_1000_Updater.getAndSet(this, 0); + val.histogram_1000_10000 = histogram_1000_10000_Updater.getAndSet(this, 0); + val.histogram_10000_100000 = histogram_10000_100000_Updater.getAndSet(this, 0); + val.histogram_100000_1000000 = histogram_100000_1000000_Updater.getAndSet(this, 0); + val.histogram_1000000_more = histogram_1000000_more_Updater.getAndSet(this, 0); + + val.lastSlowParameters = lastSlowParameters; + lastSlowParameters = null; + + val.inTransactionCount = inTransactionCountUpdater.getAndSet(this, 0); + val.resultSetHoldTimeNano = resultSetHoldTimeNanoUpdater.getAndSet(this, 0); + val.executeAndResultSetHoldTime = executeAndResultSetHoldTimeUpdater.getAndSet(this, 0); + + val.fetchRowCount_0_1 = fetchRowCount_0_1_Updater.getAndSet(this, 0); + val.fetchRowCount_1_10 = fetchRowCount_1_10_Updater.getAndSet(this, 0); + val.fetchRowCount_10_100 = fetchRowCount_10_100_Updater.getAndSet(this, 0); + val.fetchRowCount_100_1000 = fetchRowCount_100_1000_Updater.getAndSet(this, 0); + val.fetchRowCount_1000_10000 = fetchRowCount_1000_10000_Updater.getAndSet(this, 0); + val.fetchRowCount_10000_more = fetchRowCount_10000_more_Updater.getAndSet(this, 0); + + val.updateCount_0_1 = updateCount_0_1_Updater.getAndSet(this, 0); + val.updateCount_1_10 = updateCount_1_10_Updater.getAndSet(this, 0); + val.updateCount_10_100 = updateCount_10_100_Updater.getAndSet(this, 0); + val.updateCount_100_1000 = updateCount_100_1000_Updater.getAndSet(this, 0); + val.updateCount_1000_10000 = updateCount_1000_10000_Updater.getAndSet(this, 0); + val.updateCount_10000_more = updateCount_10000_more_Updater.getAndSet(this, 0); + + val.executeAndResultHoldTime_0_1 = executeAndResultHoldTime_0_1_Updater.getAndSet(this, 0); + val.executeAndResultHoldTime_1_10 = executeAndResultHoldTime_1_10_Updater.getAndSet(this, 0); + val.executeAndResultHoldTime_10_100 = executeAndResultHoldTime_10_100_Updater.getAndSet(this, 0); + val.executeAndResultHoldTime_100_1000 = executeAndResultHoldTime_100_1000_Updater.getAndSet(this, 0); + val.executeAndResultHoldTime_1000_10000 = executeAndResultHoldTime_1000_10000_Updater.getAndSet(this, 0); + val.executeAndResultHoldTime_10000_100000 = executeAndResultHoldTime_10000_100000_Updater.getAndSet(this, 0); + val.executeAndResultHoldTime_100000_1000000 = executeAndResultHoldTime_100000_1000000_Updater.getAndSet(this, 0); + val.executeAndResultHoldTime_1000000_more = executeAndResultHoldTime_1000000_more_Updater.getAndSet(this, 0); + + val.blobOpenCount = blobOpenCountUpdater.getAndSet(this, 0); + val.clobOpenCount = clobOpenCountUpdater.getAndSet(this, 0); + val.readStringLength = readStringLengthUpdater.getAndSet(this, 0); + val.readBytesLength = readBytesLengthUpdater.getAndSet(this, 0); + val.inputStreamOpenCount = inputStreamOpenCountUpdater.getAndSet(this, 0); + val.readerOpenCount = readerOpenCountUpdater.getAndSet(this, 0); + + return val; + } + public long getConcurrentMax() { return concurrentMax; } diff --git a/src/main/java/com/alibaba/druid/stat/JdbcSqlStatValue.java b/src/main/java/com/alibaba/druid/stat/JdbcSqlStatValue.java new file mode 100644 index 0000000000..751aafb0cf --- /dev/null +++ b/src/main/java/com/alibaba/druid/stat/JdbcSqlStatValue.java @@ -0,0 +1,150 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.stat; + +public class JdbcSqlStatValue { + + public String sql; + public long id; + public String dataSource; + public long executeLastStartTime; + + public long executeBatchSizeTotal; + public int executeBatchSizeMax; + + public long executeSuccessCount; + public long executeSpanNanoTotal; + public long executeSpanNanoMax; + public int runningCount; + public int concurrentMax; + public long resultSetHoldTimeNano; + public long executeAndResultSetHoldTime; + + public String name; + public String file; + public String dbType; + + public long executeNanoSpanMaxOccurTime; + + public long executeErrorCount; + public Throwable executeErrorLast; + public long executeErrorLastTime; + + public long updateCount; + public long updateCountMax; + public long fetchRowCount; + public long fetchRowCountMax; + + public long inTransactionCount; + + public String lastSlowParameters; + + public long clobOpenCount; + public long blobOpenCount; + public long readStringLength; + public long readBytesLength; + + public long inputStreamOpenCount; + public long readerOpenCount; + + public long histogram_0_1; + public long histogram_1_10; + public int histogram_10_100; + public int histogram_100_1000; + public int histogram_1000_10000; + public int histogram_10000_100000; + public int histogram_100000_1000000; + public int histogram_1000000_more; + + public long[] getExecuteHistogram() { + return new long[] { histogram_0_1, // + histogram_1_10, // + histogram_10_100, // + histogram_100_1000, // + histogram_1000_10000, // + histogram_10000_100000, // + histogram_100000_1000000, // + histogram_1000000_more, // + }; + } + + public long executeAndResultHoldTime_0_1; + public long executeAndResultHoldTime_1_10; + public int executeAndResultHoldTime_10_100; + public int executeAndResultHoldTime_100_1000; + public int executeAndResultHoldTime_1000_10000; + public int executeAndResultHoldTime_10000_100000; + public int executeAndResultHoldTime_100000_1000000; + public int executeAndResultHoldTime_1000000_more; + + public long[] getExecuteAndResultHoldHistogram() { + return new long[] { executeAndResultHoldTime_0_1, // + executeAndResultHoldTime_1_10, // + executeAndResultHoldTime_10_100, // + executeAndResultHoldTime_100_1000, // + executeAndResultHoldTime_1000_10000, // + executeAndResultHoldTime_10000_100000, // + executeAndResultHoldTime_100000_1000000, // + executeAndResultHoldTime_1000000_more, // + }; + } + + public long fetchRowCount_0_1; + public long fetchRowCount_1_10; + public long fetchRowCount_10_100; + public int fetchRowCount_100_1000; + public int fetchRowCount_1000_10000; + public int fetchRowCount_10000_more; + + public long[] getFetchRowHistogram() { + return new long[] { fetchRowCount_0_1, // + fetchRowCount_1_10, // + fetchRowCount_10_100, // + fetchRowCount_100_1000, // + fetchRowCount_1000_10000, // + fetchRowCount_10000_more, // + }; + } + + public long updateCount_0_1; + public long updateCount_1_10; + public long updateCount_10_100; + public int updateCount_100_1000; + public int updateCount_1000_10000; + public int updateCount_10000_more; + + public long[] getUpdateHistogram() { + return new long[] { updateCount_0_1, // + updateCount_1_10, // + updateCount_10_100, // + updateCount_100_1000, // + updateCount_1000_10000, // + updateCount_10000_more, // + }; + } + + public long getExecuteCount() { + return executeErrorCount + executeSuccessCount; + } + + public long getExecuteMillisMax() { + return executeSpanNanoMax / (1000 * 1000); + } + + public long getExecuteMillisTotal() { + return executeSpanNanoTotal / (1000 * 1000); + } +} diff --git a/src/main/java/com/alibaba/druid/support/logging/SLF4JImpl.java b/src/main/java/com/alibaba/druid/support/logging/SLF4JImpl.java index 81fc923eaa..cc5ab7e659 100644 --- a/src/main/java/com/alibaba/druid/support/logging/SLF4JImpl.java +++ b/src/main/java/com/alibaba/druid/support/logging/SLF4JImpl.java @@ -66,6 +66,7 @@ public class SLF4JImpl implements Log { @Override public void info(String msg) { infoCount++; + log.log(null, callerFQCN, LocationAwareLogger.INFO_INT, msg, null, null); } @Override diff --git a/src/main/java/com/alibaba/druid/util/Histogram.java b/src/main/java/com/alibaba/druid/util/Histogram.java index 147c746e36..6934202b91 100644 --- a/src/main/java/com/alibaba/druid/util/Histogram.java +++ b/src/main/java/com/alibaba/druid/util/Histogram.java @@ -77,6 +77,15 @@ public class Histogram { return array; } + public long[] toArrayAndReset() { + long[] array = new long[rangeCounters.length()]; + for (int i = 0; i < rangeCounters.length(); i++) { + array[i] = rangeCounters.getAndSet(i, 0); + } + + return array; + } + public long[] getRanges() { return ranges; } diff --git a/src/main/java/com/alibaba/druid/util/JdbcSqlStatUtils.java b/src/main/java/com/alibaba/druid/util/JdbcSqlStatUtils.java index ba3ab0b267..1e9c99cb58 100644 --- a/src/main/java/com/alibaba/druid/util/JdbcSqlStatUtils.java +++ b/src/main/java/com/alibaba/druid/util/JdbcSqlStatUtils.java @@ -16,6 +16,7 @@ package com.alibaba.druid.util; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.Map; import com.alibaba.druid.stat.JdbcSqlStat; @@ -41,4 +42,19 @@ public class JdbcSqlStatUtils { return null; } } + + public static long[] rtrim(long[] array) { + int notZeroLen = array.length; + for (int i = array.length - 1; i >= 0; --i, notZeroLen--) { + if (array[i] != 0) { + break; + } + } + + if (notZeroLen != array.length) { + array = Arrays.copyOf(array, notZeroLen); + } + + return array; + } } diff --git a/src/test/java/com/alibaba/druid/bvt/pool/TestOnBorrowFileAndNameTest.java b/src/test/java/com/alibaba/druid/bvt/pool/TestOnBorrowFileAndNameTest.java index d520a83716..60caa98cc1 100644 --- a/src/test/java/com/alibaba/druid/bvt/pool/TestOnBorrowFileAndNameTest.java +++ b/src/test/java/com/alibaba/druid/bvt/pool/TestOnBorrowFileAndNameTest.java @@ -5,9 +5,10 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Iterator; -import junit.framework.Assert; import junit.framework.TestCase; +import org.junit.Assert; + import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.stat.DruidDataSourceStatManager; import com.alibaba.druid.stat.JdbcSqlStat; @@ -24,6 +25,7 @@ public class TestOnBorrowFileAndNameTest extends TestCase { dataSource.setName("com.alibaba.dragoon.monitor"); dataSource.setMinIdle(0); dataSource.setPoolPreparedStatements(false); + dataSource.setTestOnBorrow(true); dataSource.setValidationQuery("SELECT 1"); dataSource.setFilters("stat"); diff --git a/src/test/java/com/alibaba/druid/bvt/pool/TimeBetweenLogStatsMillisTest.java b/src/test/java/com/alibaba/druid/bvt/pool/TimeBetweenLogStatsMillisTest.java new file mode 100644 index 0000000000..7179724e94 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/TimeBetweenLogStatsMillisTest.java @@ -0,0 +1,30 @@ +package com.alibaba.druid.bvt.pool; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.util.JdbcUtils; + +public class TimeBetweenLogStatsMillisTest extends TestCase { + + private DruidDataSource dataSource; + + protected void setUp() throws Exception { + dataSource = new DruidDataSource(); + + dataSource.setUrl("jdbc:mock:xxx"); + dataSource.setTimeBetweenLogStatsMillis(1000); + // dataSource.setFilters("log4j"); + } + + protected void tearDown() throws Exception { + JdbcUtils.close(dataSource); + } + + public void test_0() throws Exception { + dataSource.init(); + Assert.assertEquals(1000, dataSource.getTimeBetweenLogStatsMillis()); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/pool/TimeBetweenLogStatsMillisTest2.java b/src/test/java/com/alibaba/druid/bvt/pool/TimeBetweenLogStatsMillisTest2.java new file mode 100644 index 0000000000..5eef91891b --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/TimeBetweenLogStatsMillisTest2.java @@ -0,0 +1,30 @@ +package com.alibaba.druid.bvt.pool; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.util.JdbcUtils; + +public class TimeBetweenLogStatsMillisTest2 extends TestCase { + + private DruidDataSource dataSource; + + protected void setUp() throws Exception { + System.setProperty("druid.timeBetweenLogStatsMillis", "1000"); + + dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:xxx"); + } + + protected void tearDown() throws Exception { + JdbcUtils.close(dataSource); + System.clearProperty("druid.timeBetweenLogStatsMillis"); + } + + public void test_0() throws Exception { + dataSource.init(); + Assert.assertEquals(1000, dataSource.getTimeBetweenLogStatsMillis()); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/pool/TimeBetweenLogStatsMillisTest3.java b/src/test/java/com/alibaba/druid/bvt/pool/TimeBetweenLogStatsMillisTest3.java new file mode 100644 index 0000000000..4373f3b495 --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/pool/TimeBetweenLogStatsMillisTest3.java @@ -0,0 +1,46 @@ +package com.alibaba.druid.bvt.pool; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.util.JdbcUtils; + +public class TimeBetweenLogStatsMillisTest3 extends TestCase { + + private DruidDataSource dataSource; + + protected void setUp() throws Exception { + System.setProperty("druid.timeBetweenLogStatsMillis", "10"); + + dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:xxx"); + dataSource.setFilters("stat"); + } + + protected void tearDown() throws Exception { + JdbcUtils.close(dataSource); + System.clearProperty("druid.timeBetweenLogStatsMillis"); + } + + public void test_0() throws Exception { + dataSource.init(); + for (int i = 0; i < 10; ++i) { + Connection conn = dataSource.getConnection(); + PreparedStatement stmt = conn.prepareStatement("select ?"); + stmt.setString(1, "aaa"); + ResultSet rs = stmt.executeQuery(); + rs.close(); + stmt.close(); + conn.close(); + + Thread.sleep(10); + } + Assert.assertEquals(10, dataSource.getTimeBetweenLogStatsMillis()); + } +} diff --git a/src/test/java/com/alibaba/druid/bvt/proxy/filter/MergeStatFilterTest_tddl_2.java b/src/test/java/com/alibaba/druid/bvt/proxy/filter/MergeStatFilterTest_tddl_2.java new file mode 100644 index 0000000000..7918fe4e3b --- /dev/null +++ b/src/test/java/com/alibaba/druid/bvt/proxy/filter/MergeStatFilterTest_tddl_2.java @@ -0,0 +1,78 @@ +/* + * Copyright 1999-2011 Alibaba Group Holding Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.druid.bvt.proxy.filter; + +import java.sql.Connection; +import java.sql.Statement; +import java.util.Set; + +import javax.management.openmbean.TabularData; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.stat.DruidDataSourceStatManager; +import com.alibaba.druid.stat.JdbcStatManager; +import com.alibaba.druid.support.json.JSONUtils; +import com.alibaba.druid.util.JdbcUtils; + +public class MergeStatFilterTest_tddl_2 extends TestCase { + + private DruidDataSource dataSource; + + protected void setUp() throws Exception { + TabularData sqlList = JdbcStatManager.getInstance().getSqlList(); + if (sqlList.size() > 0) { + for (Object item : JdbcStatManager.getInstance().getSqlList().values()) { + String text = JSONUtils.toJSONString(item); + System.out.println(text); + } + } + + Assert.assertEquals(0, JdbcStatManager.getInstance().getSqlList().size()); + + dataSource = new DruidDataSource(); + dataSource.setUrl("jdbc:mock:xx"); + dataSource.setFilters("mergeStat"); + dataSource.setDbType("mysql"); + } + + protected void tearDown() throws Exception { + JdbcUtils.close(dataSource); + + Assert.assertEquals(0, JdbcStatManager.getInstance().getSqlList().size()); + } + + public void test_merge() throws Exception { + for (int i = 1000; i < 2000; ++i) { + String tableName = "t" + i; + String sql = "insert into " + tableName + " (fid, fname) values (?, ?)"; + Connection conn = dataSource.getConnection(); + + Statement stmt = conn.createStatement(); + stmt.execute(sql); + stmt.close(); + + conn.close(); + } + + Assert.assertEquals(1, dataSource.getDataSourceStat().getSqlStatMap().size()); + + } + +}