From b8e3247cb948b694d61703371ad61e54917bf650 Mon Sep 17 00:00:00 2001 From: "Saravanakumar A. Srinivasan" Date: Wed, 28 Oct 2015 21:05:14 -0700 Subject: [PATCH] Enable Hystrix Dashboard to monitor multiple independent applications Create a list of hystrix streams to monitor and monitor them independently using a monitor page. Backward compatible with existing monitor web url bookmarks. Implements #906. --- .../hystrixCommand/hystrixCommand.css | 2 +- .../hystrixCommand/hystrixCommand.js | 6 +- .../templates/hystrixCircuitContainer.html | 12 +- .../hystrixThreadPool/hystrixThreadPool.js | 6 +- .../templates/hystrixThreadPoolContainer.html | 6 +- .../src/main/webapp/css/global.css | 13 +- hystrix-dashboard/src/main/webapp/index.css | 16 ++ hystrix-dashboard/src/main/webapp/index.html | 47 ++-- .../src/main/webapp/monitor/monitor.html | 243 ++++++++++-------- 9 files changed, 208 insertions(+), 143 deletions(-) create mode 100644 hystrix-dashboard/src/main/webapp/index.css diff --git a/hystrix-dashboard/src/main/webapp/components/hystrixCommand/hystrixCommand.css b/hystrix-dashboard/src/main/webapp/components/hystrixCommand/hystrixCommand.css index d4d8787e5..b7279448f 100644 --- a/hystrix-dashboard/src/main/webapp/components/hystrixCommand/hystrixCommand.css +++ b/hystrix-dashboard/src/main/webapp/components/hystrixCommand/hystrixCommand.css @@ -110,7 +110,7 @@ .dependencies div.monitor { width: 245px; /* we want a fixed width instead of percentage as I want the boxes to be a set size and then fill in as many as can fit in each row ... this allows 3 columns on an iPad */ - height: 155px; + height: 160px; } .dependencies .success { diff --git a/hystrix-dashboard/src/main/webapp/components/hystrixCommand/hystrixCommand.js b/hystrix-dashboard/src/main/webapp/components/hystrixCommand/hystrixCommand.js index 755c8f3b9..557047fbf 100644 --- a/hystrix-dashboard/src/main/webapp/components/hystrixCommand/hystrixCommand.js +++ b/hystrix-dashboard/src/main/webapp/components/hystrixCommand/hystrixCommand.js @@ -19,7 +19,7 @@ * * Publish this externally as "HystrixCommandMonitor" */ - window.HystrixCommandMonitor = function(containerId, args) { + window.HystrixCommandMonitor = function(index, containerId, args) { var self = this; // keep scope under control self.args = args; @@ -27,6 +27,7 @@ self.args = {}; } + this.index = index; this.containerId = containerId; /** @@ -66,6 +67,7 @@ /* public */ self.eventSourceMessageListener = function(e) { var data = JSON.parse(e.data); if(data) { + data.index = self.index; // check for reportingHosts (if not there, set it to 1 for singleHost vs cluster) if(!data.reportingHosts) { data.reportingHosts = 1; @@ -91,7 +93,7 @@ // assert all the values we need validateData(data); // escape string used in jQuery & d3 selectors - data.escapedName = data.name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\$1'); + data.escapedName = data.name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\$1') + '_' + data.index; // do math convertAllAvg(data); calcRatePerSecond(data); diff --git a/hystrix-dashboard/src/main/webapp/components/hystrixCommand/templates/hystrixCircuitContainer.html b/hystrix-dashboard/src/main/webapp/components/hystrixCommand/templates/hystrixCircuitContainer.html index 91407c197..494a3b61d 100644 --- a/hystrix-dashboard/src/main/webapp/components/hystrixCommand/templates/hystrixCircuitContainer.html +++ b/hystrix-dashboard/src/main/webapp/components/hystrixCommand/templates/hystrixCircuitContainer.html @@ -1,4 +1,4 @@ -
+
<% var displayName = name; var toolTip = ""; @@ -8,12 +8,12 @@ } %> -
+
<% if(includeDetailIcon) { %>

style="padding-right:16px"> <%= displayName %> - +

<% } else { %>

><%= displayName %>

@@ -22,12 +22,12 @@
-
+
diff --git a/hystrix-dashboard/src/main/webapp/components/hystrixThreadPool/hystrixThreadPool.js b/hystrix-dashboard/src/main/webapp/components/hystrixThreadPool/hystrixThreadPool.js index a5003bc49..595b3ce57 100644 --- a/hystrix-dashboard/src/main/webapp/components/hystrixThreadPool/hystrixThreadPool.js +++ b/hystrix-dashboard/src/main/webapp/components/hystrixThreadPool/hystrixThreadPool.js @@ -19,10 +19,11 @@ * * Publish this externally as "HystrixThreadPoolMonitor" */ - window.HystrixThreadPoolMonitor = function(containerId) { + window.HystrixThreadPoolMonitor = function(index, containerId) { var self = this; // keep scope under control + this.index = index; this.containerId = containerId; /** @@ -61,6 +62,7 @@ /* public */ self.eventSourceMessageListener = function(e) { var data = JSON.parse(e.data); if(data) { + data.index = self.index; // check for reportingHosts (if not there, set it to 1 for singleHost vs cluster) if(!data.reportingHosts) { data.reportingHosts = 1; @@ -83,7 +85,7 @@ function preProcessData(data) { validateData(data); // escape string used in jQuery & d3 selectors - data.escapedName = data.name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\$1'); + data.escapedName = data.name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\$1') + '_' + data.index; // do math converAllAvg(data); calcRatePerSecond(data); diff --git a/hystrix-dashboard/src/main/webapp/components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html b/hystrix-dashboard/src/main/webapp/components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html index 035ae845e..209e52de3 100644 --- a/hystrix-dashboard/src/main/webapp/components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html +++ b/hystrix-dashboard/src/main/webapp/components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html @@ -1,4 +1,4 @@ -
+
<% var displayName = name; @@ -9,7 +9,7 @@ } %> -
+

><%= displayName %>

@@ -24,7 +24,7 @@ <% } %> var y = 200; /* escape with two backslashes */ - var vis = d3.select("#chart_THREAD_POOL_<%= name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\\\$1') %>").append("svg:svg").attr("width", "100%").attr("height", "100%"); + var vis = d3.select("#chart_THREAD_POOL_<%= name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\\\$1') + '_' + index %>").append("svg:svg").attr("width", "100%").attr("height", "100%"); /* add a circle -- we don't use the data point, we set it manually, so just passing in [1] */ var circle = vis.selectAll("circle").data([1]).enter().append("svg:circle"); /* setup the initial styling and sizing of the circle */ diff --git a/hystrix-dashboard/src/main/webapp/css/global.css b/hystrix-dashboard/src/main/webapp/css/global.css index b58e2df15..6a2ef2a73 100644 --- a/hystrix-dashboard/src/main/webapp/css/global.css +++ b/hystrix-dashboard/src/main/webapp/css/global.css @@ -12,14 +12,18 @@ img { height: auto; } - #header { background: #FFFFFF url(../images/hystrix-logo-tagline-tiny.png) no-repeat scroll 99% 0%; height: 65px; margin-bottom: 5px; } -#header h2 { +#streamHeader { + height: 65px; + margin-bottom: 5px; +} + +#streamHeader h2 { float:left; color: black; position:relative; @@ -56,7 +60,6 @@ img { font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; } - @media screen and (min-width: 1500px) { #header .header_nav { @@ -68,4 +71,8 @@ img { background: #FFFFFF url(../images/hystrix-logo-tagline-tiny.png) no-repeat scroll 99% 50%; height: 65px; } + + #streamHeader { + height: 65px; + } } diff --git a/hystrix-dashboard/src/main/webapp/index.css b/hystrix-dashboard/src/main/webapp/index.css new file mode 100644 index 000000000..0b6344e04 --- /dev/null +++ b/hystrix-dashboard/src/main/webapp/index.css @@ -0,0 +1,16 @@ +table { + width: 80%; + border-collapse: collapse; +} + +table, td { + border: 1px solid #DBDBDB; +} + +table tr:nth-child(even) { + background-color: #ECECEC; +} + +table tr:nth-child(odd) { + background-color: #FFFFFF; +} diff --git a/hystrix-dashboard/src/main/webapp/index.html b/hystrix-dashboard/src/main/webapp/index.html index dafafb61a..e4a0787de 100644 --- a/hystrix-dashboard/src/main/webapp/index.html +++ b/hystrix-dashboard/src/main/webapp/index.html @@ -4,32 +4,43 @@ Hystrix Dashboard + + + - + + + @@ -29,119 +30,14 @@ - - -
-
- -
-
Loading ...
- -
- -
- -
-
Loading ...
-
- - + +
+ var hystrixStreams = []; + + function addStreams() { + var urlVars = getUrlVars(); + var streams = urlVars.streams ? JSON.parse(decodeURIComponent(urlVars.streams)) : + urlVars.stream ? [{ + stream: decodeURIComponent(urlVars.stream), + delay: urlVars.delay, + name: decodeURIComponent(urlVars.title), + auth: urlVars.authorization + }] : []; + + _.map(streams, function(s, i) { + var dependenciesId = 'dependencies_' + i; + + var hystrixMonitor = new HystrixCommandMonitor(i, dependenciesId, {includeDetailIcon:false}); + var dependencyThreadPoolMonitor = new HystrixThreadPoolMonitor(i, 'dependencyThreadPools_' + i); + + hystrixStreams[i] = { + titleName: s.name || s.stream, + hystrixMonitor: hystrixMonitor, + dependencyThreadPoolMonitor: dependencyThreadPoolMonitor + }; + + // sort by error+volume by default + hystrixMonitor.sortByErrorThenVolume(); + dependencyThreadPoolMonitor.sortByVolume(); + + var origin; + if(s != undefined) { + origin = s.stream; + + if(s.delay) { + origin = origin + "&delay=" + s.delay; + } + + //do not show authorization in stream title + if(s.auth) { + origin = origin + "&authorization=" + s.auth; + } + } + + var proxyStream = "../proxy.stream?origin=" + origin; + + // start the EventSource which will open a streaming connection to the server + var source = new EventSource(proxyStream); + + // add the listener that will process incoming events + source.addEventListener('message', hystrixMonitor.eventSourceMessageListener, false); + source.addEventListener('message', dependencyThreadPoolMonitor.eventSourceMessageListener, false); + + // source.addEventListener('open', function(e) { + // console.console.log(">>> opened connection, phase: " + e.eventPhase); + // // Connection was opened. + // }, false); + + source.addEventListener('error', function(e) { + $("#" + dependenciesId + " .loading").html("Unable to connect to Command Metric Stream."); + $("#" + dependenciesId + " .loading").addClass("failed"); + if (e.eventPhase == EventSource.CLOSED) { + // Connection was closed. + console.log("Connection was closed on error: " + e); + } else { + console.log("Error occurred while streaming: " + e); + } + }, false); + }); + + addMonitors(); + } + + function addMonitors() { + $("#content").html(_.reduce(hystrixStreams, function(html, s, i) { + var hystrixMonitor = 'hystrixStreams[' + i + '].hystrixMonitor'; + var dependencyThreadPoolMonitor = 'hystrixStreams[' + i + '].dependencyThreadPoolMonitor'; + var dependenciesId = 'dependencies_' + i; + var dependencyThreadPoolsId = 'dependencyThreadPools_' + i; + var title_name = 'title_name_' + i; + + return html + + '
' + + '
' + + '

Hystrix Stream: ' + s.titleName + '

' + + '
' + + '
' + + '
' + + '' + + '
' + + '
Loading ...
' + + '
' + + + '
' + + '' + + '
' + + '
Loading ...
' + + '
' + + '
' + + '
' + + '
'; + }, '')); + } +