From 1b27e9ab597523759e07b073cd15d09ea09b0de8 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 12 Aug 2012 13:57:47 +0800 Subject: [PATCH] add webStat support --- .../alibaba/druid/filter/stat/StatFilter.java | 167 ++++++++++-------- .../druid/filter/stat/StatFilterContext.java | 26 ++- .../stat/StatFilterContextListener.java | 12 +- .../StatFilterContextListenerAdapter.java | 35 ++++ .../druid/support/http/WebStatFilter.java | 42 ++++- .../druid/support/http/stat/WebAppStat.java | 61 ++++++- .../support/http/stat/WebAppStatManager.java | 54 ++++++ .../druid/support/http/stat/WebURIStat.java | 129 +++++++++++++- 8 files changed, 433 insertions(+), 93 deletions(-) create mode 100644 src/main/java/com/alibaba/druid/filter/stat/StatFilterContextListenerAdapter.java create mode 100644 src/main/java/com/alibaba/druid/support/http/stat/WebAppStatManager.java 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 1fef008e32..e342f0ab64 100644 --- a/src/main/java/com/alibaba/druid/filter/stat/StatFilter.java +++ b/src/main/java/com/alibaba/druid/filter/stat/StatFilter.java @@ -376,6 +376,8 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { } private final void internalBeforeStatementExecute(StatementProxy statement, String sql) { + + final long startNano = System.nanoTime(); dataSourceStat.getStatementStat().beforeExecute(); @@ -383,7 +385,7 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { final ConnectionProxy connection = statement.getConnectionProxy(); final JdbcConnectionStat.Entry connectionCounter = getConnectionInfo(connection); - statementStat.setLastExecuteStartNano(System.nanoTime()); + statementStat.setLastExecuteStartNano(startNano); statementStat.setLastExecuteSql(sql); connectionCounter.setLastSql(sql); @@ -406,19 +408,23 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { sqlStat.setFile(statContext.getFile()); } + boolean inTransaction = false; + try { + inTransaction = !statement.getConnectionProxy().getAutoCommit(); + } catch (SQLException e) { + LOG.error("getAutoCommit error", e); + } + if (sqlStat != null) { sqlStat.setExecuteLastStartTime(System.currentTimeMillis()); sqlStat.incrementRunningCount(); - try { - boolean inTransaction = !statement.getConnectionProxy().getAutoCommit(); - if (inTransaction) { - sqlStat.incrementInTransactionCount(); - } - } catch (SQLException e) { - LOG.error("getAutoCommit error", e); + if (inTransaction) { + sqlStat.incrementInTransactionCount(); } } + + StatFilterContext.getInstance().executeBefore(sql, inTransaction); } private final void internalAfterStatementExecute(StatementProxy statement, boolean firstResult, @@ -426,13 +432,11 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { final JdbcStatementStat.Entry entry = getStatementInfo(statement); - long nowNano = System.nanoTime(); - - long nanoSpan = nowNano - entry.getLastExecuteStartNano(); + final long nowNano = System.nanoTime(); + final long nanoSpan = nowNano - entry.getLastExecuteStartNano(); dataSourceStat.getStatementStat().afterExecute(nanoSpan); - // // SQL final JdbcSqlStat sqlStat = statement.getSqlStat(); if (sqlStat != null) { @@ -456,68 +460,7 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { long millis = nanoSpan / (1000 * 1000); if (millis >= slowSqlMillis) { - StringBuilder buf = new StringBuilder(); - buf.append('['); - int index = 0; - for (JdbcParameter parameter : statement.getParameters().values()) { - if (index != 0) { - buf.append(','); - } - Object value = parameter.getValue(); - if (value == null) { - buf.append("null"); - } else if (value instanceof String) { - buf.append('"'); - String text = (String) value; - if (text.length() > 100) { - for (int i = 0; i < 97; ++i) { - char ch = text.charAt(i); - if (ch == '\'') { - buf.append('\\'); - buf.append(ch); - } else { - buf.append(ch); - } - } - buf.append("..."); - } else { - for (int i = 0; i < text.length(); ++i) { - char ch = text.charAt(i); - if (ch == '\'') { - buf.append('\\'); - buf.append(ch); - } else { - buf.append(ch); - } - } - } - buf.append('"'); - } else if (value instanceof Number) { - buf.append(value.toString()); - } else if (value instanceof java.util.Date) { - java.util.Date date = (java.util.Date) value; - buf.append(date.getClass().getSimpleName()); - buf.append('('); - buf.append(date.getTime()); - buf.append(')'); - } else if (value instanceof Boolean) { - buf.append(value.toString()); - } else if (value instanceof InputStream) { - buf.append(""); - } else if (value instanceof Clob) { - buf.append(""); - } else if (value instanceof NClob) { - buf.append(""); - } else if (value instanceof Blob) { - buf.append(""); - } else { - buf.append('<'); - buf.append(value.getClass().getName()); - buf.append('>'); - } - index++; - } - buf.append(']'); + StringBuilder buf = buildSlowParameters(statement); sqlStat.setLastSlowParameters(buf.toString()); if (logSlowSql) { @@ -526,8 +469,11 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { } } } + + String sql = statement.getLastExecuteSql(); + StatFilterContext.getInstance().executeAfter(sql, nanoSpan, null); } - + @Override protected void statement_executeErrorAfter(StatementProxy statement, String sql, Throwable error) { @@ -550,8 +496,74 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { sqlStat.addExecuteTime(statement.getLastExecuteType(), statement.isFirstResultSet(), nanoSpan); statement.setLastExecuteTimeNano(nanoSpan); } + + StatFilterContext.getInstance().executeAfter(sql, nanoSpan, error); + } - super.statement_executeErrorAfter(statement, sql, error); + private StringBuilder buildSlowParameters(StatementProxy statement) { + StringBuilder buf = new StringBuilder(); + buf.append('['); + int index = 0; + for (JdbcParameter parameter : statement.getParameters().values()) { + if (index != 0) { + buf.append(','); + } + Object value = parameter.getValue(); + if (value == null) { + buf.append("null"); + } else if (value instanceof String) { + buf.append('"'); + String text = (String) value; + if (text.length() > 100) { + for (int i = 0; i < 97; ++i) { + char ch = text.charAt(i); + if (ch == '\'') { + buf.append('\\'); + buf.append(ch); + } else { + buf.append(ch); + } + } + buf.append("..."); + } else { + for (int i = 0; i < text.length(); ++i) { + char ch = text.charAt(i); + if (ch == '\'') { + buf.append('\\'); + buf.append(ch); + } else { + buf.append(ch); + } + } + } + buf.append('"'); + } else if (value instanceof Number) { + buf.append(value.toString()); + } else if (value instanceof java.util.Date) { + java.util.Date date = (java.util.Date) value; + buf.append(date.getClass().getSimpleName()); + buf.append('('); + buf.append(date.getTime()); + buf.append(')'); + } else if (value instanceof Boolean) { + buf.append(value.toString()); + } else if (value instanceof InputStream) { + buf.append(""); + } else if (value instanceof Clob) { + buf.append(""); + } else if (value instanceof NClob) { + buf.append(""); + } else if (value instanceof Blob) { + buf.append(""); + } else { + buf.append('<'); + buf.append(value.getClass().getName()); + buf.append('>'); + } + index++; + } + buf.append(']'); + return buf; } @Override @@ -571,7 +583,7 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { dataSourceStat.getResultSetStat().afterClose(nanoSpan); dataSourceStat.getResultSetStat().addFetchRowCount(fetchRowCount); dataSourceStat.getResultSetStat().incrementCloseCounter(); - + StatFilterContext.getInstance().addFetchRowCount(fetchRowCount); String sql = resultSet.getSql(); @@ -840,5 +852,4 @@ public class StatFilter extends FilterEventAdapter implements StatFilterMBean { } } - } diff --git a/src/main/java/com/alibaba/druid/filter/stat/StatFilterContext.java b/src/main/java/com/alibaba/druid/filter/stat/StatFilterContext.java index f2d821f281..767360d566 100644 --- a/src/main/java/com/alibaba/druid/filter/stat/StatFilterContext.java +++ b/src/main/java/com/alibaba/druid/filter/stat/StatFilterContext.java @@ -5,9 +5,9 @@ import java.util.concurrent.CopyOnWriteArrayList; public class StatFilterContext { - private List listeners = new CopyOnWriteArrayList(); + private List listeners = new CopyOnWriteArrayList(); - private static final StatFilterContext instance = new StatFilterContext(); + private static final StatFilterContext instance = new StatFilterContext(); public final static StatFilterContext getInstance() { return instance; @@ -16,7 +16,7 @@ public class StatFilterContext { public void addContextListener(StatFilterContextListener listener) { this.listeners.add(listener); } - + public boolean removeContextListener(StatFilterContextListener listener) { return listeners.remove(listener); } @@ -25,17 +25,33 @@ public class StatFilterContext { return listeners; } - protected void addUpdateCount(int updateCount) { + public 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) { + public void addFetchRowCount(int fetchRowCount) { for (int i = 0; i < listeners.size(); ++i) { StatFilterContextListener listener = listeners.get(i); listener.addFetchRowCount(fetchRowCount); } } + + public void executeBefore(String sql, boolean inTransaction) { + + } + + public void executeAfter(String sql, long nanoSpan, Throwable error) { + + } + + public void commit() { + + } + + public void rollback() { + + } } diff --git a/src/main/java/com/alibaba/druid/filter/stat/StatFilterContextListener.java b/src/main/java/com/alibaba/druid/filter/stat/StatFilterContextListener.java index c402c982eb..00370690d9 100644 --- a/src/main/java/com/alibaba/druid/filter/stat/StatFilterContextListener.java +++ b/src/main/java/com/alibaba/druid/filter/stat/StatFilterContextListener.java @@ -1,8 +1,16 @@ package com.alibaba.druid.filter.stat; - public interface StatFilterContextListener { + void addUpdateCount(int updateCount); - + void addFetchRowCount(int fetchRowCount); + + void executeBefore(String sql, boolean inTransaction); + + void executeAfter(String sql, long nanoSpan, Throwable error); + + void commit(); + + void rollback(); } diff --git a/src/main/java/com/alibaba/druid/filter/stat/StatFilterContextListenerAdapter.java b/src/main/java/com/alibaba/druid/filter/stat/StatFilterContextListenerAdapter.java new file mode 100644 index 0000000000..df60e2ee92 --- /dev/null +++ b/src/main/java/com/alibaba/druid/filter/stat/StatFilterContextListenerAdapter.java @@ -0,0 +1,35 @@ +package com.alibaba.druid.filter.stat; + +public class StatFilterContextListenerAdapter implements StatFilterContextListener { + + @Override + public void addUpdateCount(int updateCount) { + + } + + @Override + public void addFetchRowCount(int fetchRowCount) { + + } + + @Override + public void executeBefore(String sql, boolean inTransaction) { + + } + + @Override + public void executeAfter(String sql, long nanoSpan, Throwable error) { + + } + + @Override + public void commit() { + + } + + @Override + public void rollback() { + + } + +} diff --git a/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java b/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java index f9a5519fda..54f0d4608d 100644 --- a/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java +++ b/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java @@ -11,8 +11,9 @@ 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.filter.stat.StatFilterContextListenerAdapter; import com.alibaba.druid.support.http.stat.WebAppStat; +import com.alibaba.druid.support.http.stat.WebAppStatManager; import com.alibaba.druid.support.http.stat.WebURIStat; public class WebStatFilter implements Filter { @@ -55,18 +56,24 @@ public class WebStatFilter implements Filter { @Override public void init(FilterConfig config) throws ServletException { config.getServletContext().getContextPath(); - + StatFilterContext.getInstance().addContextListener(statFilterContextListener); webAppStat = new WebAppStat(); + + WebAppStatManager.getInstance().addWebAppStatSet(webAppStat); } @Override public void destroy() { StatFilterContext.getInstance().removeContextListener(statFilterContextListener); + + if (webAppStat != null) { + WebAppStatManager.getInstance().remove(webAppStat); + } } - class WebStatFilterContextListener implements StatFilterContextListener { + class WebStatFilterContextListener extends StatFilterContextListenerAdapter { @Override public void addUpdateCount(int updateCount) { @@ -83,5 +90,34 @@ public class WebStatFilter implements Filter { stat.addJdbcFetchRowCount(fetchRowCount); } } + + @Override + public void executeBefore(String sql, boolean inTransaction) { + WebURIStat stat = WebURIStat.current(); + if (stat != null) { + stat.incrementJdbcExecuteCount(); + } + } + + @Override + public void executeAfter(String sql, long nanoSpan, Throwable error) { + + } + + @Override + public void commit() { + WebURIStat stat = WebURIStat.current(); + if (stat != null) { + stat.incrementJdbcCommitCount(); + } + } + + @Override + public void rollback() { + WebURIStat stat = WebURIStat.current(); + if (stat != null) { + stat.incrementJdbcRollbackCount(); + } + } } } 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 index 4282e405cc..c35c800564 100644 --- a/src/main/java/com/alibaba/druid/support/http/stat/WebAppStat.java +++ b/src/main/java/com/alibaba/druid/support/http/stat/WebAppStat.java @@ -1,5 +1,9 @@ package com.alibaba.druid.support.http.stat; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; @@ -18,7 +22,7 @@ public class WebAppStat { private final AtomicInteger runningCount = new AtomicInteger(); private final AtomicInteger concurrentMax = new AtomicInteger(); - private final AtomicLong count = new AtomicLong(0); + private final AtomicLong requestCount = new AtomicLong(0); private final static ThreadLocal currentLocal = new ThreadLocal(); @@ -26,10 +30,24 @@ public class WebAppStat { private final AtomicLong uriStatMapFullCount = new AtomicLong(); + private String contextPath; + public static WebAppStat current() { return currentLocal.get(); } + public WebAppStat(){ + + } + + public WebAppStat(String contextPath){ + this.contextPath = contextPath; + } + + public String getContextPath() { + return contextPath; + } + public void beforeInvoke(String uri) { currentLocal.set(this); @@ -48,7 +66,7 @@ public class WebAppStat { } } - count.incrementAndGet(); + requestCount.incrementAndGet(); WebURIStat uriStat = getURIStat(uri); @@ -97,4 +115,43 @@ public class WebAppStat { this.maxStatUriCount = maxStatUriCount; } + public int getRunningCount() { + return this.runningCount.get(); + } + + public long getConcurrentMax() { + return concurrentMax.get(); + } + + public long getRequestCount() { + return requestCount.get(); + } + + public Map getStatData() { + Map data = new LinkedHashMap(); + + data.put("ContextPath", this.getContextPath()); + data.put("RunningCount", this.getRunningCount()); + data.put("ConcurrentMax", this.getConcurrentMax()); + data.put("RequestCount", this.getRequestCount()); + + List> uriStatDataList = new ArrayList>(this.uriStatMap.size()); + for (WebURIStat uriStat : this.uriStatMap.values()) { + Map uriStatData = uriStat.getStatData(); + + long runningCount = (Long) uriStatData.get("RunningCount"); + long requestCount = (Long) uriStatData.get("RequestCount"); + + if (runningCount == 0 && requestCount == 0) { + continue; + } + + uriStatDataList.add(uriStatData); + } + + data.put("URIList", uriStatDataList); + + return data; + } + } diff --git a/src/main/java/com/alibaba/druid/support/http/stat/WebAppStatManager.java b/src/main/java/com/alibaba/druid/support/http/stat/WebAppStatManager.java new file mode 100644 index 0000000000..5d50952da8 --- /dev/null +++ b/src/main/java/com/alibaba/druid/support/http/stat/WebAppStatManager.java @@ -0,0 +1,54 @@ +package com.alibaba.druid.support.http.stat; + +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +public class WebAppStatManager { + + public final static String SYS_PROP_INSTANCES = "druid.web.webAppStat"; + + private final static WebAppStatManager instance = new WebAppStatManager(); + + private Set webAppStatSet = null; + + public static WebAppStatManager getInstance() { + return instance; + } + + public Set getWebAppStatSet() { + if (webAppStatSet == null) { + webAppStatSet = getWebAppStatSet0(); + } + + return webAppStatSet; + } + + public void addWebAppStatSet(Object webAppStat) { + getWebAppStatSet().add(webAppStat); + } + + public boolean remove(Object webAppStat) { + return getWebAppStatSet().remove(webAppStat); + } + + @SuppressWarnings("unchecked") + static Set getWebAppStatSet0() { + Properties properties = System.getProperties(); + Set webAppStats = (Set) properties.get(SYS_PROP_INSTANCES); + + if (webAppStats == null) { + synchronized (properties) { + webAppStats = (Set) properties.get(SYS_PROP_INSTANCES); + + if (webAppStats == null) { + webAppStats = new CopyOnWriteArraySet(); + properties.put(SYS_PROP_INSTANCES, webAppStats); + } + } + } + + return webAppStats; + } + +} 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 index 51836cc6f5..e93337f83d 100644 --- a/src/main/java/com/alibaba/druid/support/http/stat/WebURIStat.java +++ b/src/main/java/com/alibaba/druid/support/http/stat/WebURIStat.java @@ -1,24 +1,39 @@ package com.alibaba.druid.support.http.stat; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; public class WebURIStat { + static class RequestStat { + + long jdbcExecuteCount; + long jdbcUpdateCount; + long jdbcFetchRowCount; + } + 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 requestCount = new AtomicLong(0); private final AtomicLong jdbcFetchRowCount = new AtomicLong(); - private final AtomicLong jdbcFetchRowPeak = new AtomicLong(); + private final AtomicLong jdbcFetchRowPeak = new AtomicLong(); // 单次请求读取行数的峰值 + private final AtomicLong jdbcUpdateCount = new AtomicLong(); + private final AtomicLong jdbcUpdatePeak = new AtomicLong(); // 单次请求更新行数的峰值 + private final AtomicLong jdbcExecuteCount = new AtomicLong(); + private final AtomicLong jdbcExecutePeak = new AtomicLong(); // 单次请求执行SQL次数的峰值 + private final AtomicLong jdbcCommitCount = new AtomicLong(); private final AtomicLong jdbcRollbackCount = new AtomicLong(); private final static ThreadLocal currentLocal = new ThreadLocal(); + private static ThreadLocal localRequestStat = new ThreadLocal(); public WebURIStat(String uri){ super(); @@ -35,6 +50,7 @@ public class WebURIStat { public void beforeInvoke(String uri) { currentLocal.set(this); + localRequestStat.set(new RequestStat()); int running = runningCount.incrementAndGet(); @@ -51,17 +67,83 @@ public class WebURIStat { } } - count.incrementAndGet(); + requestCount.incrementAndGet(); } public void afterInvoke(long nanoSpan) { runningCount.decrementAndGet(); + { + RequestStat localStat = localRequestStat.get(); + if (localStat != null) { + { + long fetchRowCount = localStat.jdbcFetchRowCount; + + for (;;) { + long peak = jdbcFetchRowPeak.get(); + if (fetchRowCount <= peak) { + break; + } + + if (jdbcFetchRowPeak.compareAndSet(peak, fetchRowCount)) { + break; + } + } + } + { + long executeCount = localStat.jdbcExecuteCount; + + for (;;) { + long peak = jdbcExecutePeak.get(); + if (executeCount <= peak) { + break; + } + + if (jdbcExecutePeak.compareAndSet(peak, executeCount)) { + break; + } + } + } + { + long updateCount = localStat.jdbcUpdateCount; + + for (;;) { + long peak = jdbcUpdatePeak.get(); + if (updateCount <= peak) { + break; + } + + if (jdbcUpdatePeak.compareAndSet(peak, updateCount)) { + break; + } + } + } + } + } + currentLocal.set(null); + localRequestStat.set(null); + } + + public int getRunningCount() { + return this.runningCount.get(); + } + + public long getConcurrentMax() { + return concurrentMax.get(); + } + + public long getRequestCount() { + return requestCount.get(); } public void addJdbcFetchRowCount(long delta) { this.jdbcFetchRowCount.addAndGet(delta); + + RequestStat localStat = localRequestStat.get(); + if (localStat != null) { + localStat.jdbcFetchRowCount += delta; + } } public long getJdbcFetchRowCount() { @@ -74,20 +156,38 @@ public class WebURIStat { public void addJdbcUpdateCount(int updateCount) { this.jdbcUpdateCount.addAndGet(updateCount); + + RequestStat localStat = localRequestStat.get(); + if (localStat != null) { + localStat.jdbcExecuteCount += updateCount; + } } public long getJdbcUpdateCount() { return jdbcUpdateCount.get(); } + public long getJdbcUpdatePeak() { + return jdbcUpdatePeak.get(); + } + public void incrementJdbcExecuteCount() { jdbcExecuteCount.incrementAndGet(); + + RequestStat localStat = localRequestStat.get(); + if (localStat != null) { + localStat.jdbcExecuteCount++; + } } public long getJdbcExecuteCount() { return jdbcExecuteCount.get(); } + public long getJdbcExecutePeak() { + return jdbcExecutePeak.get(); + } + public void incrementJdbcCommitCount() { jdbcCommitCount.incrementAndGet(); } @@ -103,4 +203,27 @@ public class WebURIStat { public long getJdbcRollbackCount() { return jdbcRollbackCount.get(); } + + public Map getStatData() { + Map data = new LinkedHashMap(); + + data.put("URI", this.getUri()); + data.put("RunningCount", this.getRunningCount()); + data.put("ConcurrentMax", this.getConcurrentMax()); + data.put("RequestCount", this.getRequestCount()); + + data.put("JdbcCommitCount", this.getJdbcCommitCount()); + data.put("JdbcRollbackCount", this.getJdbcRollbackCount()); + + data.put("JdbcExecuteCount", this.getJdbcExecuteCount()); + data.put("JdbcExecutePeak", this.getJdbcExecutePeak()); + + data.put("JdbcFetchRowCount", this.getJdbcFetchRowCount()); + data.put("JdbcFetchRowPeak", this.getJdbcFetchRowPeak()); + + data.put("JdbcUpdateCount", this.getJdbcUpdateCount()); + data.put("JdbcUpdatePeak", this.getJdbcUpdatePeak()); + + return data; + } }