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