diff --git a/src/main/java/com/alibaba/druid/proxy/DruidDriver.java b/src/main/java/com/alibaba/druid/proxy/DruidDriver.java index 637f7205ef..e9591ae6b8 100644 --- a/src/main/java/com/alibaba/druid/proxy/DruidDriver.java +++ b/src/main/java/com/alibaba/druid/proxy/DruidDriver.java @@ -1,17 +1,10 @@ /* - * 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. + * 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.proxy; @@ -55,6 +48,7 @@ public class DruidDriver implements Driver, DruidDriverMBean { private final static ConcurrentMap proxyDataSources = new ConcurrentHashMap(); private final static AtomicInteger dataSourceIdSeed = new AtomicInteger(0); + private final static AtomicInteger sqlStatIdSeed = new AtomicInteger(0); public final static String DEFAULT_PREFIX = "jdbc:wrap-jdbc:"; public final static String DRIVER_PREFIX = "driver="; @@ -100,7 +94,7 @@ public class DruidDriver implements Driver, DruidDriverMBean { return false; } - public DruidDriver(){ + public DruidDriver() { } @@ -112,6 +106,10 @@ public class DruidDriver implements Driver, DruidDriverMBean { return dataSourceIdSeed.incrementAndGet(); } + public static int createSqlStatId() { + return sqlStatIdSeed.incrementAndGet(); + } + @Override public boolean acceptsURL(String url) throws SQLException { if (url == null) { diff --git a/src/main/java/com/alibaba/druid/stat/JdbcSqlStat.java b/src/main/java/com/alibaba/druid/stat/JdbcSqlStat.java index 34bb6b145e..bb62006916 100644 --- a/src/main/java/com/alibaba/druid/stat/JdbcSqlStat.java +++ b/src/main/java/com/alibaba/druid/stat/JdbcSqlStat.java @@ -1,17 +1,10 @@ /* - * 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. + * 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; @@ -27,6 +20,7 @@ import javax.management.openmbean.CompositeType; import javax.management.openmbean.OpenType; import javax.management.openmbean.SimpleType; +import com.alibaba.druid.proxy.DruidDriver; import com.alibaba.druid.proxy.jdbc.StatementExecuteType; import com.alibaba.druid.util.Histogram; import com.alibaba.druid.util.IOUtils; @@ -42,63 +36,57 @@ public final class JdbcSqlStat implements JdbcSqlStatMBean { private String dataSource; private long executeLastStartTime; - private final AtomicLong executeBatchSizeTotal = new AtomicLong(); - private final AtomicLong executeBatchSizeMax = new AtomicLong(); + private final AtomicLong executeBatchSizeTotal = new AtomicLong(); + private final AtomicLong executeBatchSizeMax = new AtomicLong(); + + private final AtomicLong executeSuccessCount = new AtomicLong(); + private final AtomicLong executeSpanNanoTotal = new AtomicLong(); + private final AtomicLong executeSpanNanoMax = new AtomicLong(); + private final AtomicLong runningCount = new AtomicLong(0L); + private final AtomicLong concurrentMax = new AtomicLong(); + private final AtomicLong resultSetHoldTimeNano = new AtomicLong(); + private final AtomicLong executeAndResultSetHoldTime = new AtomicLong(); - private final AtomicLong executeSuccessCount = new AtomicLong(); - private final AtomicLong executeSpanNanoTotal = new AtomicLong(); - private final AtomicLong executeSpanNanoMax = new AtomicLong(); - private final AtomicLong runningCount = new AtomicLong(0L); - private final AtomicLong concurrentMax = new AtomicLong(); - private final AtomicLong resultSetHoldTimeNano = new AtomicLong(); - private final AtomicLong executeAndResultSetHoldTime = new AtomicLong(); - private String name; private String file; private String dbType; private long executeNanoSpanMaxOccurTime; - private final AtomicLong executeErrorCount = new AtomicLong(); + private final AtomicLong executeErrorCount = new AtomicLong(); private Throwable executeErrorLast; private long executeErrorLastTime; - private final AtomicLong updateCount = new AtomicLong(); - private final AtomicLong fetchRowCount = new AtomicLong(); + private final AtomicLong updateCount = new AtomicLong(); + private final AtomicLong fetchRowCount = new AtomicLong(); - private final AtomicLong inTransactionCount = new AtomicLong(); + private final AtomicLong inTransactionCount = new AtomicLong(); - private String lastSlowParameters; - private final Histogram histogram = new Histogram(new long[] { // - // + private final Histogram histogram = new Histogram(new long[] { // + // 1, 10, 100, 1000, 10 * 1000, // 100 * 1000, 1000 * 1000 - // - }); - - private final Histogram executeAndResultHoldTimeHistogram = new Histogram(new long[] { // - // - 1, 10, 100, 1000, 10 * 1000, // - 100 * 1000, 1000 * 1000 -// - }); + // + }); - - private final Histogram fetchRowCountHistogram = new Histogram(new long[] { // - 1, 10, 100, 1000, 10 * 1000 - }); + private final Histogram executeAndResultHoldTimeHistogram = new Histogram(new long[] { // + // + 1, 10, 100, 1000, 10 * 1000, // + 100 * 1000, 1000 * 1000 + // + }); - - private final Histogram updateCountHistogram = new Histogram(new long[] { // - 1, 10, 100, 1000, 10 * 1000 - }); - - - - public JdbcSqlStat(String sql){ + private final Histogram fetchRowCountHistogram = new Histogram(new long[] { // + 1, 10, 100, 1000, 10 * 1000 }); + + private final Histogram updateCountHistogram = new Histogram(new long[] { // + 1, 10, 100, 1000, 10 * 1000 }); + + public JdbcSqlStat(String sql) { this.sql = sql; + this.id = DruidDriver.createSqlStatId(); } public String getLastSlowParameters() { @@ -164,7 +152,7 @@ public final class JdbcSqlStat implements JdbcSqlStatMBean { context.setFile(val); } - + public final static void setContextSql(String val) { JdbcStatContext context = JdbcStatManager.getInstance().getStatContext(); if (context == null) { @@ -233,10 +221,10 @@ public final class JdbcSqlStat implements JdbcSqlStatMBean { if (delta > 0) { this.updateCount.addAndGet(delta); } - + this.updateCountHistogram.record(delta); this.fetchRowCountHistogram.record(0); - + } public long getUpdateCount() { @@ -288,7 +276,7 @@ public final class JdbcSqlStat implements JdbcSqlStatMBean { public void addFetchRowCount(long delta) { this.fetchRowCount.addAndGet(delta); this.fetchRowCountHistogram.record(delta); - + } public void addExecuteBatchCount(long batchSize) { @@ -333,7 +321,7 @@ public final class JdbcSqlStat implements JdbcSqlStatMBean { } } } - + public void decrementRunningCount() { runningCount.decrementAndGet(); } @@ -345,12 +333,12 @@ public final class JdbcSqlStat implements JdbcSqlStatMBean { public long getExecuteSuccessCount() { return executeSuccessCount.get(); } - + public void addExecuteTime(StatementExecuteType executeType, long nanoSpan) { - if (StatementExecuteType.ExecuteQuery != executeType) { - executeAndResultHoldTimeHistogram.record((nanoSpan)/1000/1000); - } - addExecuteTime(nanoSpan); + if (StatementExecuteType.ExecuteQuery != executeType) { + executeAndResultHoldTimeHistogram.record((nanoSpan) / 1000 / 1000); + } + addExecuteTime(nanoSpan); } public void addExecuteTime(long nanoSpan) { @@ -454,12 +442,12 @@ public final class JdbcSqlStat implements JdbcSqlStatMBean { SimpleType.LONG, // SimpleType.LONG, // new ArrayType(SimpleType.LONG, true), // - - // 30 - - + + // 30 - + new ArrayType(SimpleType.LONG, true), // new ArrayType(SimpleType.LONG, true), // - + }; String[] indexNames = { @@ -500,17 +488,11 @@ public final class JdbcSqlStat implements JdbcSqlStatMBean { // 25 - 29 "Histogram", // - "LastSlowParameters", - "ResultSetHoldTime", - "ExecuteAndResultSetHoldTime", - "FetchRowCountHistogram", - + "LastSlowParameters", "ResultSetHoldTime", "ExecuteAndResultSetHoldTime", "FetchRowCountHistogram", + // 30 - "EffectedRowCountHistogram", - "ExecuteAndResultHoldTimeHistogram" - - - + "EffectedRowCountHistogram", "ExecuteAndResultHoldTimeHistogram" + // }; String[] indexDescriptions = indexNames; @@ -575,15 +557,13 @@ public final class JdbcSqlStat implements JdbcSqlStatMBean { map.put("ResultSetHoldTime", getResultSetHoldTimeMilis()); // 27 map.put("ExecuteAndResultSetHoldTime", this.getExecuteAndResultSetHoldTimeMilis()); // 28 map.put("FetchRowCountHistogram", this.getFetchRowCountHistogram().toArray()); // 29 - - + map.put("EffectedRowCountHistogram", this.getUpdateCountHistogram().toArray()); // 30 map.put("ExecuteAndResultHoldTimeHistogram", this.getExecuteAndResultHoldTimeHistogram().toArray()); // 30 - return map; } - + public Histogram getHistogram() { return this.histogram; } @@ -606,25 +586,23 @@ public final class JdbcSqlStat implements JdbcSqlStatMBean { public long getResultSetHoldTimeMilis() { return getResultSetHoldTimeNano() / (1000 * 1000); } - + public long getExecuteAndResultSetHoldTimeMilis() { return getExecuteAndResultSetHoldTimeNano() / (1000 * 1000); } - - + public Histogram getFetchRowCountHistogram() { return this.fetchRowCountHistogram; } - + public Histogram getUpdateCountHistogram() { return this.updateCountHistogram; } - + public Histogram getExecuteAndResultHoldTimeHistogram() { return this.executeAndResultHoldTimeHistogram; } - - + public long getResultSetHoldTimeNano() { return resultSetHoldTimeNano.get(); } @@ -632,15 +610,15 @@ public final class JdbcSqlStat implements JdbcSqlStatMBean { public long getExecuteAndResultSetHoldTimeNano() { return executeAndResultSetHoldTime.get(); } - + public void addResultSetHoldTimeNano(long nano) { resultSetHoldTimeNano.addAndGet(nano); } public void addResultSetHoldTimeNano(long statementExecuteNano, long resultHoldTimeNano) { - resultSetHoldTimeNano.addAndGet(resultHoldTimeNano); - executeAndResultSetHoldTime.addAndGet(statementExecuteNano+resultHoldTimeNano); - executeAndResultHoldTimeHistogram.record((statementExecuteNano+resultHoldTimeNano)/1000/1000); - updateCountHistogram.record(0); + resultSetHoldTimeNano.addAndGet(resultHoldTimeNano); + executeAndResultSetHoldTime.addAndGet(statementExecuteNano + resultHoldTimeNano); + executeAndResultHoldTimeHistogram.record((statementExecuteNano + resultHoldTimeNano) / 1000 / 1000); + updateCountHistogram.record(0); } } diff --git a/src/main/java/com/alibaba/druid/support/http/StatServlet.java b/src/main/java/com/alibaba/druid/support/http/StatViewSerlvet.java similarity index 65% rename from src/main/java/com/alibaba/druid/support/http/StatServlet.java rename to src/main/java/com/alibaba/druid/support/http/StatViewSerlvet.java index ed9fa0968c..0a3addd5bd 100644 --- a/src/main/java/com/alibaba/druid/support/http/StatServlet.java +++ b/src/main/java/com/alibaba/druid/support/http/StatViewSerlvet.java @@ -1,7 +1,6 @@ package com.alibaba.druid.support.http; import java.io.IOException; -import java.io.InputStream; import java.io.PrintWriter; import java.sql.Driver; import java.sql.DriverManager; @@ -18,32 +17,50 @@ import javax.servlet.http.HttpServletResponse; import com.alibaba.druid.VERSION; import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.parser.SQLStatementParser; +import com.alibaba.druid.sql.visitor.SchemaStatVisitor; import com.alibaba.druid.stat.DruidDataSourceStatManager; import com.alibaba.druid.stat.JdbcSqlStat; import com.alibaba.druid.stat.JdbcStatManager; import com.alibaba.druid.util.IOUtils; -import com.alibaba.druid.util.JdbcUtils; import com.alibaba.druid.util.StringUtils; import com.alibaba.fastjson.JSON; /** * @author sandzhang */ -public class StatServlet extends HttpServlet { +public class StatViewSerlvet extends HttpServlet { /** * */ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private final static int RESULT_CODE_SUCCESS = 1; - private final static int RESULT_CODE_ERROR = -1; + private final static int RESULT_CODE_SUCCESS = 1; + private final static int RESULT_CODE_ERROR = -1; + + private final static String RESOURCE_PATH = "support/http/resources"; + private final static String TEMPLATE_PAGE_RESOURCE_PATH = RESOURCE_PATH + "/template.html"; + + public String templatePage; + + public void init() throws ServletException { + try { + templatePage = IOUtils.readFromResource(TEMPLATE_PAGE_RESOURCE_PATH); + } catch (IOException e) { + throw new ServletException("error read templatePage:" + TEMPLATE_PAGE_RESOURCE_PATH, e); + } + } public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String contextPath = request.getContextPath(); String servletPath = request.getServletPath(); String requestURI = request.getRequestURI(); + response.setCharacterEncoding("utf-8"); + if (contextPath == null) { // root context contextPath = ""; } @@ -59,7 +76,7 @@ public class StatServlet extends HttpServlet { returnJSONBasicStat(request, response); return; } - + if (path.equals("/reset-all.json")) { resetAllStat(); returnJSONResult(request, response, RESULT_CODE_SUCCESS, null); @@ -70,7 +87,7 @@ public class StatServlet extends HttpServlet { returnJSONResult(request, response, RESULT_CODE_SUCCESS, getJSONDataSourceStatList()); return; } - + if (path.startsWith("/datasource-")) { Integer id = StringUtils.subStringToInteger(path, "datasource-", "."); Object result = getJSONDataSourceStat(id); @@ -82,11 +99,20 @@ public class StatServlet extends HttpServlet { returnJSONResult(request, response, RESULT_CODE_SUCCESS, getJSONSqlStat()); return; } - + if (path.startsWith("/sql-")) { Integer id = StringUtils.subStringToInteger(path, "sql-", "."); - Object result = getJSONSqlStat(id); - returnJSONResult(request, response, result == null ? RESULT_CODE_ERROR : RESULT_CODE_SUCCESS, result); + if (path.endsWith(".json")) { + Object result = getJSONSqlStat(id); + returnJSONResult(request, response, result == null ? RESULT_CODE_ERROR : RESULT_CODE_SUCCESS, result); + return; + } + + if (path.endsWith(".html")) { + JdbcSqlStat sqlStat = getSqlStatById(id); + returnViewSqlStat(sqlStat, response); + return; + } return; } @@ -94,6 +120,67 @@ public class StatServlet extends HttpServlet { returnResourceFile(path, response); } + private void returnViewSqlStat(JdbcSqlStat sqlStat, HttpServletResponse response) throws IOException { + if (sqlStat == null) return; + + StringBuilder content = new StringBuilder(); + + content.append("

FULL SQL

" + sqlStat.getSql() + "

"); + content.append("

Format View:

"); + content.append("
"); + content.append("

API:com.alibaba.druid.sql.SQLUtils.format(sql,DBType);

"); + content.append("
"); + + SQLStatementParser parser = new SQLStatementParser(sqlStat.getSql()); + List statementList = parser.parseStatementList(); + if (!statementList.isEmpty()) { + content.append("

Parse View:

"); + + SQLStatement statemen = statementList.get(0); + SchemaStatVisitor visitor = new SchemaStatVisitor(); + statemen.accept(visitor); + content.append(""); + content.append(""); + content.append(""); + content.append(""); + content.append(""); + + content.append(""); + content.append(""); + content.append(""); + content.append(""); + + content.append(""); + content.append(""); + content.append(""); + content.append(""); + + content.append(""); + content.append(""); + content.append(""); + content.append(""); + content.append("
Tables" + visitor.getTables() + "
Fields" + visitor.getColumns() + "
Coditions" + visitor.getConditions() + "
Relationships" + visitor.getRelationships() + "
"); + + content.append("
"); + content.append("

API:

"); + content.append("

"); + content.append("SQLStatementParser parser = new SQLStatementParser(sqlStat.getSql());
"); + content.append("List statementList = parser.parseStatementList();
"); + content.append("SQLStatement statemen = statementList.get(0);
"); + content.append("SchemaStatVisitor visitor = new SchemaStatVisitor();
"); + content.append("SchemaStatVisitor visitor = new SchemaStatVisitor();
"); + content.append("statemen.accept(visitor);
"); + content.append("visitor.getTables() / visitor.getColumns() / visitor.getConditions() / visitor.getRelationships()
"); + content.append("

"); + content.append("
"); + } + + response.getWriter().print(mergeTemplatePage("Druid Sql View", content.toString())); + + } + private void resetAllStat() { JdbcStatManager.getInstance().reset(); DruidDataSourceStatManager.getInstance().reset(); @@ -174,6 +261,7 @@ public class StatServlet extends HttpServlet { private Map toJSONSqlStat(JdbcSqlStat sqlStat) { Map json = new LinkedHashMap(); + json.put("ID", sqlStat.getId()); json.put("SQL", sqlStat.getSql()); json.put("File", sqlStat.getFile()); json.put("Name", sqlStat.getName()); @@ -241,19 +329,11 @@ public class StatServlet extends HttpServlet { } private void returnResourceFile(String fileName, HttpServletResponse response) throws ServletException, IOException { - InputStream in = null; - try { - in = Thread.currentThread().getContextClassLoader().getResourceAsStream("support/http/resources" + fileName); - if (in == null) { - return; - } + String text = IOUtils.readFromResource(RESOURCE_PATH + fileName); + response.getWriter().write(text); + } - String text = IOUtils.read(in); - response.getWriter().write(text); - } catch (IOException e) { - throw new ServletException("error when response static file: " + fileName, e); - } finally { - JdbcUtils.close(in); - } + private String mergeTemplatePage(String title, String content) { + return templatePage.replaceAll("\\{title\\}", title).replaceAll("\\{content\\}", content); } } diff --git a/src/main/resources/support/http/resources/api.html b/src/main/resources/support/http/resources/api.html index 6093f675b4..18018ac198 100644 --- a/src/main/resources/support/http/resources/api.html +++ b/src/main/resources/support/http/resources/api.html @@ -3,7 +3,8 @@ Druid Stat JSON API - + +
diff --git a/src/main/resources/support/http/resources/datasource.html b/src/main/resources/support/http/resources/datasource.html index a555ccf6ce..841f472f38 100644 --- a/src/main/resources/support/http/resources/datasource.html +++ b/src/main/resources/support/http/resources/datasource.html @@ -2,8 +2,9 @@ Druid DataSourceStat + - + + + + + +
+ +
+
+ {content} +
+ + \ No newline at end of file