diff --git a/src/main/java/com/alibaba/druid/support/http/StatServlet.java b/src/main/java/com/alibaba/druid/support/http/StatServlet.java new file mode 100644 index 0000000000..ed17c6412e --- /dev/null +++ b/src/main/java/com/alibaba/druid/support/http/StatServlet.java @@ -0,0 +1,251 @@ +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; +import java.util.Enumeration; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.alibaba.druid.VERSION; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.stat.DruidDataSourceStatManager; +import com.alibaba.druid.stat.JdbcSqlStat; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +/** + * @author sandzhang + */ +public class StatServlet extends HttpServlet { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private final static int RESULT_CODE_SUCCESS = 1; + private final static int RESULT_CODE_ERROR = -1; + + private static JarFile druidJarFile; + + public void init() throws ServletException { + try { + druidJarFile = new JarFile(StatServlet.class.getProtectionDomain().getCodeSource().getLocation().getPath()); + } catch (IOException e) { + throw new ServletException("error to contruct druid JarFile Object ", e); + } + } + + public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String requestPath = req.getPathInfo(); + + if (requestPath.startsWith("/json/basic")) { + returnJSON_BasicStat(req, resp); + return; + } + if (requestPath.startsWith("/json/datasource")) { + returnJSON_DataSourceStat(req, resp); + return; + } + if (requestPath.startsWith("/json/sql")) { + returnJSON_DataSourceSqlStat(req, resp); + return; + } + // find file in jar resources path + returnResourceFile(requestPath, resp); + return; + } + + private void returnResourceFile(String fileName, HttpServletResponse resp) throws ServletException, IOException { + PrintWriter out = resp.getWriter(); + // TODO cache the file + out.print(readResourceFile(fileName)); + } + + private JSONArray getJSONDrivers() { + JSONArray drivers = new JSONArray(); + for (Enumeration e = DriverManager.getDrivers(); e.hasMoreElements();) { + Driver driver = e.nextElement(); + drivers.add(driver.getClass().getName()); + } + return drivers; + } + + private void returnJSON_BasicStat(HttpServletRequest req, HttpServletResponse resp) throws IOException { + JSONObject json = new JSONObject(); + json.put("Version", VERSION.getVersionNumber()); + json.put("Drivers", getJSONDrivers()); + json.put("DataSources", getJSONDataSources()); + + returnJSONResult(req, resp, RESULT_CODE_SUCCESS, json); + + } + + private void returnJSON_DataSourceStat(HttpServletRequest req, HttpServletResponse resp) throws IOException { + Integer identity = null; + try { + identity = Integer.parseInt(req.getParameter("identity")); + } catch (Exception e) { + } + JSONObject json = getJSONDataSourceStat(identity); + if (identity == null) { + returnJSONResult(req, resp, RESULT_CODE_ERROR, null); + } else { + returnJSONResult(req, resp, RESULT_CODE_SUCCESS, json); + } + + } + + private void returnJSON_DataSourceSqlStat(HttpServletRequest req, HttpServletResponse resp) throws IOException { + Integer identity = null; + try { + identity = Integer.parseInt(req.getParameter("identity")); + } catch (Exception e) { + } + JSONArray re = getJSONDataSourceSqlStat(identity); + if (identity == null || re == null) { + returnJSONResult(req, resp, RESULT_CODE_ERROR, null); + } else { + returnJSONResult(req, resp, RESULT_CODE_SUCCESS, re); + } + + } + + private JSONArray getJSONDataSources() { + JSONArray drivers = new JSONArray(); + for (DruidDataSource dataSource : DruidDataSourceStatManager.getDruidDataSourceInstances()) { + drivers.add(getJSONDataSource(dataSource)); + } + return drivers; + } + + private JSONObject getJSONDataSourceStat(Integer id) { + if (id == null) { + return null; + } + DruidDataSource ds = getDruidDataSourceById(id); + if (ds == null) { + return null; + } + + return getJSONDataSource(ds); + } + + private DruidDataSource getDruidDataSourceById(Integer identity) { + if (identity == null) { + return null; + } + for (DruidDataSource ds : DruidDataSourceStatManager.getDruidDataSourceInstances()) { + if (System.identityHashCode(ds) == identity) { + return ds; + } + } + return null; + } + + private JSONArray getJSONDataSourceSqlStat(Integer id) { + if (id == null) { + return null; + } + DruidDataSource ds = getDruidDataSourceById(id); + if (ds == null) { + return null; + } + JSONArray array = new JSONArray(); + for (JdbcSqlStat sqlStat : ds.getDataSourceStat().getSqlStatMap().values()) { + JSONObject json = new JSONObject(); + json.put("SQL", sqlStat.getSql()); + json.put("File", sqlStat.getFile()); + json.put("Name", sqlStat.getName()); + json.put("ExecuteCount", sqlStat.getExecuteCount()); + json.put("ExecuteMillisTotal", sqlStat.getExecuteMillisTotal()); + json.put("ExecuteMillisMax", sqlStat.getExecuteMillisMax()); + json.put("InTxnCount", sqlStat.getInTransactionCount()); + json.put("ErrorCount", sqlStat.getErrorCount()); + json.put("UpdateCount", sqlStat.getUpdateCount()); + json.put("FetchRowCount", sqlStat.getFetchRowCount()); + json.put("RunningCount", sqlStat.getRunningCount()); + json.put("ConcurrentMax", sqlStat.getConcurrentMax()); + json.put("ExecHistogram", sqlStat.getHistogram().toArray()); + json.put("FetchRowHistogram", sqlStat.getFetchRowCountHistogram().toArray()); + json.put("UpdateCountHistogram", sqlStat.getUpdateCountHistogram().toArray()); + json.put("ExecAndRsHoldHistogram", sqlStat.getExecuteAndResultHoldTimeHistogram().toArray()); + + array.add(json); + } + return array; + } + + private JSONObject getJSONDataSource(DruidDataSource dataSource) { + + JSONObject json = new JSONObject(); + json.put("Identity", System.identityHashCode(dataSource)); + json.put("Name", dataSource.getName()); + json.put("URL", dataSource.getUrl()); + json.put("DbType", dataSource.getDbType()); + json.put("UserName", dataSource.getUsername()); + json.put("DriverClassName", dataSource.getDriverClassName()); + json.put("InitialSize", dataSource.getInitialSize()); + json.put("MinIdle", dataSource.getMinIdle()); + json.put("MaxActive", dataSource.getMaxActive()); + json.put("TestOnBorrow", dataSource.isTestOnBorrow()); + json.put("TestWhileIdle", dataSource.isTestWhileIdle()); + json.put("LogicConnectCount", dataSource.getConnectCount()); + json.put("LogicCloseCount", dataSource.getCloseCount()); + json.put("LogicConnectErrorCount", dataSource.getConnectErrorCount()); + json.put("PhysicalConnectCount", dataSource.getCreateCount()); + json.put("PhysicalCloseCount", dataSource.getDestroyCount()); + json.put("PhysicalConnectErrorCount", dataSource.getCreateErrorCount()); + json.put("PSCacheAccessCount", dataSource.getCachedPreparedStatementAccessCount()); + json.put("PSCacheHitCount", dataSource.getCachedPreparedStatementHitCount()); + json.put("PSCacheMissCount", dataSource.getCachedPreparedStatementMissCount()); + return json; + } + + private void returnJSONResult(HttpServletRequest req, HttpServletResponse resp, int resultCode, JSON jsonObject) + throws IOException { + PrintWriter out = resp.getWriter(); + + JSONObject json = new JSONObject(); + json.put("ResultCode", resultCode); + json.put("Content", jsonObject); + out.print(json.toJSONString()); + } + + private String readResourceFile(String fileName) throws ServletException { + ZipEntry fileEntry = druidJarFile.getEntry("support/http/resources" + fileName); + if (fileEntry == null) { + return null; + } + + InputStream in = null; + byte[] result = null; + try { + in = druidJarFile.getInputStream(fileEntry); + + int size = (int) fileEntry.getSize(); + result = new byte[size]; + int offer = 0; + while (offer < size) + offer += in.read(result, offer, size - offer); + } catch (IOException e) { + if (in != null) { + try { + in.close(); + } catch (IOException e1) { + } + } + throw new ServletException("error read file: " + fileName, e); + } + return result == null ? null : new String(result); + } +} diff --git a/src/main/resources/support/http/resources/index.html b/src/main/resources/support/http/resources/index.html new file mode 100644 index 0000000000..f491562a6a --- /dev/null +++ b/src/main/resources/support/http/resources/index.html @@ -0,0 +1,92 @@ + + + + Druid Stat + + + + +
+
+ + + + + + + + + +
+ Version + + +
+ drivers + + +
+
+
+ +
+

Basic Info :

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
UserName URL 
DbType DriverClassName 
TestOnBorrow TestWhileIdle 
InitialSize MinIdle MaxActive 
LogicConnectCount LogicCloseCount LogicConnectErrorCount 
PhysicalConnectCount PhysicalCloseCount PhysicalConnectErrorCount 
PSCacheAccessCount PSCacheHitCount PSCacheMissCount 
+
+

SqlStat Info

+ + + + + + + + + + + + + + + + +
SQLFileNameExecute CountExecute Millis TotalExecute Millis MaxInTxn CountError CountEffected Row CountFetch Row CountRunning CountConcurrent MaxHistogram
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/support/http/resources/js.js b/src/main/resources/support/http/resources/js.js new file mode 100644 index 0000000000..0793d7c6a4 --- /dev/null +++ b/src/main/resources/support/http/resources/js.js @@ -0,0 +1,152 @@ + +var datasourceId; +var xmlHttpForBasicInfo; +var xmlHttpForDataSourceInfo; +var xmlHttpForDataSourceSqlStatInfo; +function ajaxRequestForBasicInfo(){ + if(window.XMLHttpRequest) xmlHttpForBasicInfo=new XMLHttpRequest(); + else if(window.ActiveXObject) xmlHttpForBasicInfo=new ActiveXObject("Microsoft.XMLHTTP"); + xmlHttpForBasicInfo.onreadystatechange=ajaxResponseForBasicInfo; + xmlHttpForBasicInfo.open("GET",'json/basic',true); + xmlHttpForBasicInfo.send(null); +} +function ajaxRequestForDataSourceSqlStatInfo(){ + if(!datasourceId){ + return; + } + if(window.XMLHttpRequest) xmlHttpForDataSourceSqlStatInfo=new XMLHttpRequest(); + else if(window.ActiveXObject) xmlHttpForDataSourceSqlStatInfo=new ActiveXObject("Microsoft.XMLHTTP"); + xmlHttpForDataSourceSqlStatInfo.onreadystatechange=ajaxResponseForDataSourceSqlStatInfo; + xmlHttpForDataSourceSqlStatInfo.open("GET",'json/sql?identity='+datasourceId,true); + xmlHttpForDataSourceSqlStatInfo.send(null); +} +function ajaxRequestForDataSourceInfo(){ + if(!datasourceId){ + return; + } + if(window.XMLHttpRequest) xmlHttpForDataSourceInfo=new XMLHttpRequest(); + else if(window.ActiveXObject) xmlHttpForDataSourceInfo=new ActiveXObject("Microsoft.XMLHTTP"); + xmlHttpForDataSourceInfo.onreadystatechange=ajaxResponseForDataSourceInfo; + xmlHttpForDataSourceInfo.open("GET",'json/datasource?identity='+datasourceId,true); + xmlHttpForDataSourceInfo.send(null); +} +function ajaxResponseForBasicInfo(){ + var html=''; + var tabHtml=''; + if(xmlHttpForBasicInfo.readyState==4){ + if(xmlHttpForBasicInfo.status==200){ + var jsonResp=eval("("+xmlHttpForBasicInfo.responseText+")"); + if(jsonResp.ResultCode==1){ + document.getElementById("DruidVersion").innerHTML=jsonResp.Content.Version; + + var driversList=jsonResp.Content.Drivers; + if(driversList){ + var driverHtml=''; + for(var i=0;i['+ds.Name+'] '; + } + } + } + document.getElementById("menu").innerHTML=tabHtml; + } + } +} +function ajaxResponseForDataSourceInfo(){ + var html=''; + if(xmlHttpForDataSourceInfo.readyState==4){ + if(xmlHttpForDataSourceInfo.status==200){ + var jsonResp=eval("("+xmlHttpForDataSourceInfo.responseText+")"); + if(jsonResp.ResultCode==1){ + var ds=jsonResp.Content; + document.getElementById("DS-Info-Title").innerHTML=ds.Name; + document.getElementById("DS-Info-UserName").innerHTML=ds.UserName; + document.getElementById("DS-Info-URL").innerHTML=ds.URL; + document.getElementById("DS-Info-DbType").innerHTML=ds.DbType; + document.getElementById("DS-Info-DriverClassName").innerHTML=ds.DriverClassName; + document.getElementById("DS-Info-TestOnBorrow").innerHTML=ds.TestOnBorrow; + document.getElementById("DS-Info-TestWhileIdle").innerHTML=ds.TestWhileIdle; + document.getElementById("DS-Info-InitialSize").innerHTML=ds.InitialSize; + document.getElementById("DS-Info-MinIdle").innerHTML=ds.MinIdle; + document.getElementById("DS-Info-MaxActive").innerHTML=ds.MaxActive; + document.getElementById("DS-Info-LogicConnectCount").innerHTML=ds.LogicConnectCount; + document.getElementById("DS-Info-LogicCloseCount").innerHTML=ds.LogicCloseCount; + document.getElementById("DS-Info-LogicConnectErrorCount").innerHTML=ds.LogicConnectErrorCount; + document.getElementById("DS-Info-PhysicalConnectCount").innerHTML=ds.PhysicalConnectCount; + document.getElementById("DS-Info-PhysicalCloseCount").innerHTML=ds.PhysicalCloseCount; + document.getElementById("DS-Info-PhysicalConnectErrorCount").innerHTML=ds.PhysicalConnectErrorCount; + document.getElementById("DS-Info-PSCacheAccessCount").innerHTML=ds.PSCacheAccessCount; + document.getElementById("DS-Info-PSCacheHitCount").innerHTML=ds.PSCacheHitCount; + document.getElementById("DS-Info-PSCacheMissCount").innerHTML=ds.PSCacheMissCount; + + } + } + } +} +function ajaxResponseForDataSourceSqlStatInfo(){ + if(xmlHttpForDataSourceSqlStatInfo.readyState==4){ + if(xmlHttpForDataSourceSqlStatInfo.status==200){ + var jsonResp=eval("("+xmlHttpForDataSourceSqlStatInfo.responseText+")"); + if(jsonResp.ResultCode==1){ + var sqlStatList=jsonResp.Content; + var sqlStatTable=document.getElementById("SqlStatTable"); + while(sqlStatTable.rows.length>1){ + sqlStatTable.deleteRow(1); + } + for(var i=0;iExecHistogram |'; + hiHtml+='FetchRow | '; + hiHtml+='UpdateCount | '; + hiHtml+='ExecAndRsHold'; + hi.innerHTML=hiHtml; + } + } + } + } +} +function showDataSource(id){ + datasourceId=id; + ajaxRequestForDataSourceInfo(); + ajaxRequestForDataSourceSqlStatInfo(); +} +function init(){ + ajaxRequestForBasicInfo(); + var time1=setInterval("ajaxRequestForBasicInfo();",1000); + var time2=setInterval("ajaxRequestForDataSourceInfo();",1000); + var time3=setInterval("ajaxRequestForDataSourceSqlStatInfo();",1000); +} \ No newline at end of file diff --git a/src/main/resources/support/http/resources/style.css b/src/main/resources/support/http/resources/style.css new file mode 100644 index 0000000000..4b9d779251 --- /dev/null +++ b/src/main/resources/support/http/resources/style.css @@ -0,0 +1,68 @@ + +body{ + font-family: 'Microsoft YaHei','Microsoft JhengHei',STHeiti,Georgia,'Times New Roman',Times,serif; + font-weight:normal; + font-size:12px; +} +#menu { + height:50px; +} + + #menu a{ + color: #0082FF; + font: 18px 'Microsoft YaHei','Microsoft JhengHei',STHeiti,Georgia,'Times New Roman',Times,serif; + } +tr{ + background-color:white; +} +th{ + background-color:#D3EAEF; +} +#main h2 { + border: 0 dashed red; + color: #0082FF; + font: 24px 'Microsoft YaHei','Microsoft JhengHei',STHeiti,Georgia,'Times New Roman',Times,serif; + margin: 0 0 10px; +} +#main h2 a{ + font-size: 13px; + color: black; +} +#main h3 { + border: 0 dashed red; + color: #0082FF; + margin: 0 0 10px; +} +table{ + background-color:#A8C7CE; + line-height:18px; +} + +.td_lable{ + background-color:#D3EAEF; + font-weight:bolder; + } + + a{ + color: #0082FF; + font: 13px 'Microsoft YaHei','Microsoft JhengHei',STHeiti,Georgia,'Times New Roman',Times,serif; + } + + li{ + line-height:18px; + } + +.bar rect { + fill: steelblue; + shape-rendering: crispEdges; +} + +.bar text { + fill: #fff; +} + +.axis path, .axis line { + fill: none; + stroke: #000; + shape-rendering: crispEdges; +} \ No newline at end of file