diff --git a/src/main/java/com/alibaba/druid/filter/stat/StatFilter.java b/src/main/java/com/alibaba/druid/filter/stat/StatFilter.java index 0e88e16577..1fef008e32 100644 --- a/src/main/java/com/alibaba/druid/filter/stat/StatFilter.java +++ b/src/main/java/com/alibaba/druid/filter/stat/StatFilter.java @@ -184,7 +184,7 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { } } } - + { String property = properties.getProperty("druid.stat.logSlowSql"); if ("true".equals(property)) { @@ -439,6 +439,7 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { sqlStat.incrementExecuteSuccessCount(); for (int updateCount : updateCountArray) { sqlStat.addUpdateCount(updateCount); + StatFilterContext.getInstance().addUpdateCount(updateCount); } sqlStat.decrementRunningCount(); @@ -518,9 +519,10 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { } buf.append(']'); sqlStat.setLastSlowParameters(buf.toString()); - + if (logSlowSql) { - LOG.error("slow sql " + millis + " millis. \n" + statement.getLastExecuteSql() + "\n" + buf.toString()); + LOG.error("slow sql " + millis + " millis. \n" + statement.getLastExecuteSql() + "\n" + + buf.toString()); } } } @@ -564,19 +566,19 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { long nanoSpan = System.nanoTime() - resultSet.getConstructNano(); - int fetchCount = resultSet.getFetchRowCount(); + int fetchRowCount = resultSet.getFetchRowCount(); dataSourceStat.getResultSetStat().afterClose(nanoSpan); - - dataSourceStat.getResultSetStat().addFetchRowCount(fetchCount); - + dataSourceStat.getResultSetStat().addFetchRowCount(fetchRowCount); dataSourceStat.getResultSetStat().incrementCloseCounter(); + + StatFilterContext.getInstance().addFetchRowCount(fetchRowCount); String sql = resultSet.getSql(); if (sql != null) { JdbcSqlStat sqlStat = resultSet.getSqlStat(); if (sqlStat != null && resultSet.getCloseCount() == 0) { - sqlStat.addFetchRowCount(fetchCount); + sqlStat.addFetchRowCount(fetchRowCount); long stmtExecuteNano = resultSet.getStatementProxy().getLastExecuteTimeNano(); sqlStat.addResultSetHoldTimeNano(stmtExecuteNano, nanoSpan); } @@ -837,4 +839,6 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { return features; } } + + } diff --git a/src/main/java/com/alibaba/druid/filter/stat/StatFilterContext.java b/src/main/java/com/alibaba/druid/filter/stat/StatFilterContext.java new file mode 100644 index 0000000000..f2d821f281 --- /dev/null +++ b/src/main/java/com/alibaba/druid/filter/stat/StatFilterContext.java @@ -0,0 +1,41 @@ +package com.alibaba.druid.filter.stat; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public class StatFilterContext { + + private List listeners = new CopyOnWriteArrayList(); + + private static final StatFilterContext instance = new StatFilterContext(); + + public final static StatFilterContext getInstance() { + return instance; + } + + public void addContextListener(StatFilterContextListener listener) { + this.listeners.add(listener); + } + + public boolean removeContextListener(StatFilterContextListener listener) { + return listeners.remove(listener); + } + + public List getListeners() { + return listeners; + } + + protected void addUpdateCount(int updateCount) { + for (int i = 0; i < listeners.size(); ++i) { + StatFilterContextListener listener = listeners.get(i); + listener.addUpdateCount(updateCount); + } + } + + protected void addFetchRowCount(int fetchRowCount) { + for (int i = 0; i < listeners.size(); ++i) { + StatFilterContextListener listener = listeners.get(i); + listener.addFetchRowCount(fetchRowCount); + } + } +} diff --git a/src/main/java/com/alibaba/druid/filter/stat/StatFilterContextListener.java b/src/main/java/com/alibaba/druid/filter/stat/StatFilterContextListener.java new file mode 100644 index 0000000000..c402c982eb --- /dev/null +++ b/src/main/java/com/alibaba/druid/filter/stat/StatFilterContextListener.java @@ -0,0 +1,8 @@ +package com.alibaba.druid.filter.stat; + + +public interface StatFilterContextListener { + void addUpdateCount(int updateCount); + + void addFetchRowCount(int fetchRowCount); +} diff --git a/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java b/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java new file mode 100644 index 0000000000..f9a5519fda --- /dev/null +++ b/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java @@ -0,0 +1,87 @@ +package com.alibaba.druid.support.http; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + +import com.alibaba.druid.filter.stat.StatFilterContext; +import com.alibaba.druid.filter.stat.StatFilterContextListener; +import com.alibaba.druid.support.http.stat.WebAppStat; +import com.alibaba.druid.support.http.stat.WebURIStat; + +public class WebStatFilter implements Filter { + + private WebAppStat webAppStat = null; + private WebStatFilterContextListener statFilterContextListener = new WebStatFilterContextListener(); + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, + ServletException { + HttpServletRequest httpRequest = (HttpServletRequest) request; + + final String requestURI = getRequestURI(httpRequest); + + if (isExclusion(requestURI)) { + chain.doFilter(request, response); + return; + } + + long startNano = System.nanoTime(); + webAppStat.beforeInvoke(requestURI); + + try { + chain.doFilter(request, response); + } finally { + long nanoSpan = System.nanoTime() - startNano; + + webAppStat.afterInvoke(nanoSpan); + } + } + + public boolean isExclusion(String uri) { + return false; + } + + public String getRequestURI(HttpServletRequest request) { + return request.getRequestURI(); + } + + @Override + public void init(FilterConfig config) throws ServletException { + config.getServletContext().getContextPath(); + + StatFilterContext.getInstance().addContextListener(statFilterContextListener); + + webAppStat = new WebAppStat(); + } + + @Override + public void destroy() { + StatFilterContext.getInstance().removeContextListener(statFilterContextListener); + } + + class WebStatFilterContextListener implements StatFilterContextListener { + + @Override + public void addUpdateCount(int updateCount) { + WebURIStat stat = WebURIStat.current(); + if (stat != null) { + stat.addJdbcUpdateCount(updateCount); + } + } + + @Override + public void addFetchRowCount(int fetchRowCount) { + WebURIStat stat = WebURIStat.current(); + if (stat != null) { + stat.addJdbcFetchRowCount(fetchRowCount); + } + } + } +} diff --git a/src/main/java/com/alibaba/druid/support/http/stat/WebAppStat.java b/src/main/java/com/alibaba/druid/support/http/stat/WebAppStat.java new file mode 100644 index 0000000000..4282e405cc --- /dev/null +++ b/src/main/java/com/alibaba/druid/support/http/stat/WebAppStat.java @@ -0,0 +1,100 @@ +package com.alibaba.druid.support.http.stat; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; + +public class WebAppStat { + + private final static Log LOG = LogFactory.getLog(WebAppStat.class); + + public final static int DEFAULT_MAX_STAT_URI_COUNT = 1000; + + private volatile int maxStatUriCount = DEFAULT_MAX_STAT_URI_COUNT; + + private final AtomicInteger runningCount = new AtomicInteger(); + private final AtomicInteger concurrentMax = new AtomicInteger(); + private final AtomicLong count = new AtomicLong(0); + + private final static ThreadLocal currentLocal = new ThreadLocal(); + + private final ConcurrentMap uriStatMap = new ConcurrentHashMap(); + + private final AtomicLong uriStatMapFullCount = new AtomicLong(); + + public static WebAppStat current() { + return currentLocal.get(); + } + + public void beforeInvoke(String uri) { + currentLocal.set(this); + + int running = runningCount.incrementAndGet(); + + for (;;) { + int max = concurrentMax.get(); + if (running > max) { + if (concurrentMax.compareAndSet(max, running)) { + break; + } else { + continue; + } + } else { + break; + } + } + + count.incrementAndGet(); + + WebURIStat uriStat = getURIStat(uri); + + if (uriStat != null) { + uriStat.beforeInvoke(uri); + } + } + + public WebURIStat getURIStat(String uri) { + if (uriStatMap.size() >= this.getMaxStatUriCount()) { + long fullCount = uriStatMapFullCount.getAndIncrement(); + + if (fullCount == 0) { + LOG.error("urlStatMap is full"); + } + + return null; + } + + WebURIStat uriStat = uriStatMap.get(uri); + + if (uriStat == null) { + uriStatMap.putIfAbsent(uri, new WebURIStat(uri)); + uriStat = uriStatMap.get(uri); + } + + return uriStat; + } + + public void afterInvoke(long nanoSpan) { + runningCount.decrementAndGet(); + + currentLocal.set(null); + + WebURIStat uriStat = WebURIStat.current(); + if (uriStat != null) { + uriStat.afterInvoke(nanoSpan); + } + } + + public int getMaxStatUriCount() { + return maxStatUriCount; + } + + public void setMaxStatUriCount(int maxStatUriCount) { + this.maxStatUriCount = maxStatUriCount; + } + +} diff --git a/src/main/java/com/alibaba/druid/support/http/stat/WebURIStat.java b/src/main/java/com/alibaba/druid/support/http/stat/WebURIStat.java new file mode 100644 index 0000000000..51836cc6f5 --- /dev/null +++ b/src/main/java/com/alibaba/druid/support/http/stat/WebURIStat.java @@ -0,0 +1,106 @@ +package com.alibaba.druid.support.http.stat; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +public class WebURIStat { + + private final String uri; + + private final AtomicInteger runningCount = new AtomicInteger(); + private final AtomicInteger concurrentMax = new AtomicInteger(); + private final AtomicLong count = new AtomicLong(0); + + private final AtomicLong jdbcFetchRowCount = new AtomicLong(); + private final AtomicLong jdbcFetchRowPeak = new AtomicLong(); + private final AtomicLong jdbcUpdateCount = new AtomicLong(); + private final AtomicLong jdbcExecuteCount = new AtomicLong(); + private final AtomicLong jdbcCommitCount = new AtomicLong(); + private final AtomicLong jdbcRollbackCount = new AtomicLong(); + + private final static ThreadLocal currentLocal = new ThreadLocal(); + + public WebURIStat(String uri){ + super(); + this.uri = uri; + } + + public static WebURIStat current() { + return currentLocal.get(); + } + + public String getUri() { + return uri; + } + + public void beforeInvoke(String uri) { + currentLocal.set(this); + + int running = runningCount.incrementAndGet(); + + for (;;) { + int max = concurrentMax.get(); + if (running > max) { + if (concurrentMax.compareAndSet(max, running)) { + break; + } else { + continue; + } + } else { + break; + } + } + + count.incrementAndGet(); + } + + public void afterInvoke(long nanoSpan) { + runningCount.decrementAndGet(); + + currentLocal.set(null); + } + + public void addJdbcFetchRowCount(long delta) { + this.jdbcFetchRowCount.addAndGet(delta); + } + + public long getJdbcFetchRowCount() { + return jdbcFetchRowCount.get(); + } + + public long getJdbcFetchRowPeak() { + return jdbcFetchRowPeak.get(); + } + + public void addJdbcUpdateCount(int updateCount) { + this.jdbcUpdateCount.addAndGet(updateCount); + } + + public long getJdbcUpdateCount() { + return jdbcUpdateCount.get(); + } + + public void incrementJdbcExecuteCount() { + jdbcExecuteCount.incrementAndGet(); + } + + public long getJdbcExecuteCount() { + return jdbcExecuteCount.get(); + } + + public void incrementJdbcCommitCount() { + jdbcCommitCount.incrementAndGet(); + } + + public long getJdbcCommitCount() { + return jdbcCommitCount.get(); + } + + public void incrementJdbcRollbackCount() { + jdbcRollbackCount.incrementAndGet(); + } + + public long getJdbcRollbackCount() { + return jdbcRollbackCount.get(); + } +}