diff --git a/pom.xml b/pom.xml index fc26742b..88be448b 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 0.6.5 1.1.147 8.6.0 - 1.4.10 + 1.4.13 2.0.0 1.0.28 1.0.11 diff --git a/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixCommand/hystrixCommand.css b/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixCommand/hystrixCommand.css index 71d5ee39..15e95ec8 100644 --- a/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixCommand/hystrixCommand.css +++ b/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/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: 150px; + height: 155px; } .dependencies .success { diff --git a/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixCommand/hystrixCommand.js b/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixCommand/hystrixCommand.js index 01fa98f0..65d6fcb0 100644 --- a/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixCommand/hystrixCommand.js +++ b/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixCommand/hystrixCommand.js @@ -16,19 +16,19 @@ /** * Object containing functions for displaying and updating the UI with streaming data. - * + * * Publish this externally as "HystrixCommandMonitor" */ window.HystrixCommandMonitor = function(containerId, args) { - + var self = this; // keep scope under control self.args = args; if(self.args == undefined) { self.args = {}; } - + this.containerId = containerId; - + /** * Initialization on construction */ @@ -36,7 +36,7 @@ var maxXaxisForCircle="40%"; var maxYaxisForCircle="40%"; var maxRadiusForCircle="125"; - + // CIRCUIT_BREAKER circle visualization settings self.circuitCircleRadius = d3.scale.pow().exponent(0.5).domain([0, 400]).range(["5", maxRadiusForCircle]); // requests per second per host self.circuitCircleYaxis = d3.scale.linear().domain([0, 400]).range(["30%", maxXaxisForCircle]); @@ -47,7 +47,7 @@ /** * We want to keep sorting in the background since data values are always changing, so this will re-sort every X milliseconds * to maintain whatever sort the user (or default) has chosen. - * + * * In other words, sorting only for adds/deletes is not sufficient as all but alphabetical sort are dynamically changing. */ setInterval(function() { @@ -55,11 +55,11 @@ self.sortSameAsLast(); }, 10000); - + /** * END of Initialization on construction */ - + /** * Event listener to handle new messages from EventSource as streamed from the server. */ @@ -70,7 +70,7 @@ if(!data.reportingHosts) { data.reportingHosts = 1; } - + if(data && data.type == 'HystrixCommand') { if (data.deleteData == 'true') { deleteCircuit(data.escapedName); @@ -82,34 +82,43 @@ }; /** - * Pre process the data before displying in the UI. - * e.g Get Averages from sums, do rate calculation etc. + * Pre process the data before displying in the UI. + * e.g Get Averages from sums, do rate calculation etc. */ function preProcessData(data) { + // set defaults for values that may be missing from older streams + setIfMissing(data, "rollingCountBadRequests", 0); + // assert all the values we need validateData(data); // escape string used in jQuery & d3 selectors data.escapedName = data.name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\$1'); // do math - converAllAvg(data); + convertAllAvg(data); calcRatePerSecond(data); } + function setIfMissing(data, key, defaultValue) { + if(data[key] == undefined) { + data[key] = defaultValue; + } + } + /** * Since the stream of data can be aggregated from multiple hosts in a tiered manner * the aggregation just sums everything together and provides us the denominator (reportingHosts) - * so we must divide by it to get an average per instance value. - * + * so we must divide by it to get an average per instance value. + * * We want to do this on any numerical values where we want per instance rather than cluster-wide sum. */ - function converAllAvg(data) { + function convertAllAvg(data) { convertAvg(data, "errorPercentage", true); convertAvg(data, "latencyExecute_mean", false); convertAvg(data, "latencyTotal_mean", false); - + // the following will break when it becomes a compound string if the property is dynamically changed convertAvg(data, "propertyValue_metricsRollingStatisticalWindowInMilliseconds", false); } - + function convertAvg(data, key, decimal) { if (decimal) { data[key] = getInstanceAverage(data[key], data["reportingHosts"], decimal); @@ -117,7 +126,7 @@ data[key] = getInstanceAverage(data[key], data["reportingHosts"], decimal); } } - + function getInstanceAverage(value, reportingHosts, decimal) { if (decimal) { return roundNumber(value/reportingHosts); @@ -125,7 +134,7 @@ return Math.floor(value/reportingHosts); } } - + function calcRatePerSecond(data) { var numberSeconds = data["propertyValue_metricsRollingStatisticalWindowInMilliseconds"] / 1000; @@ -159,6 +168,7 @@ assertNotNull(data,"rollingCountSuccess"); assertNotNull(data,"rollingCountThreadPoolRejected"); assertNotNull(data,"rollingCountTimeout"); + assertNotNull(data,"rollingCountBadRequests"); assertNotNull(data,"currentConcurrentExecutionCount"); assertNotNull(data,"latencyExecute_mean"); assertNotNull(data,"latencyExecute"); @@ -178,7 +188,7 @@ assertNotNull(data,"propertyValue_requestLogEnabled"); assertNotNull(data,"propertyValue_metricsRollingStatisticalWindowInMilliseconds"); } - + function assertNotNull(data, key) { if(data[key] == undefined) { throw new Error("Key Missing: " + key + " for " + data.name); @@ -187,25 +197,25 @@ /** * Method to display the CIRCUIT data - * + * * @param data */ /* private */ function displayCircuit(data) { - + try { preProcessData(data); } catch (err) { log("Failed preProcessData: " + err.message); return; } - + // add the 'addCommas' function to the 'data' object so the HTML templates can use it data.addCommas = addCommas; // add the 'roundNumber' function to the 'data' object so the HTML templates can use it data.roundNumber = roundNumber; // add the 'getInstanceAverage' function to the 'data' object so the HTML templates can use it data.getInstanceAverage = getInstanceAverage; - + var addNew = false; // check if we need to create the container if(!$('#CIRCUIT_' + data.escapedName).length) { @@ -215,39 +225,39 @@ }else { data.includeDetailIcon = false; } - + // it doesn't exist so add it var html = tmpl(hystrixTemplateCircuitContainer, data); // remove the loading thing first $('#' + containerId + ' span.loading').remove(); // now create the new data and add it $('#' + containerId + '').append(html); - + // add the default sparkline graph d3.selectAll('#graph_CIRCUIT_' + data.escapedName + ' svg').append("svg:path"); - + // remember this is new so we can trigger a sort after setting data addNew = true; } - - + + // now update/insert the data $('#CIRCUIT_' + data.escapedName + ' div.monitor_data').html(tmpl(hystrixTemplateCircuit, data)); - + var ratePerSecond = data.ratePerSecond; var ratePerSecondPerHost = data.ratePerSecondPerHost; var ratePerSecondPerHostDisplay = ratePerSecondPerHost; var errorThenVolume = (data.errorPercentage * 100000000) + ratePerSecond; - + // set the rates on the div element so it's available for sorting $('#CIRCUIT_' + data.escapedName).attr('rate_value', ratePerSecond); $('#CIRCUIT_' + data.escapedName).attr('error_then_volume', errorThenVolume); - + // update errorPercentage color on page $('#CIRCUIT_' + data.escapedName + ' a.errorPercentage').css('color', self.circuitErrorPercentageColorRange(data.errorPercentage)); - + updateCircle('circuit', '#CIRCUIT_' + data.escapedName + ' circle', ratePerSecondPerHostDisplay, data.errorPercentage); - + if(data.graphValues) { // we have a set of values to initialize with updateSparkline('circuit', '#CIRCUIT_' + data.escapedName + ' path', data.graphValues); @@ -260,7 +270,7 @@ self.sortSameAsLast(); } } - + /* round a number to X digits: num => the number to round, dec => the number of decimals */ /* private */ function roundNumber(num) { var dec=1; @@ -271,10 +281,10 @@ } return resultAsString; }; - - - - + + + + /* private */ function updateCircle(variablePrefix, cssTarget, rate, errorPercentage) { var newXaxisForCircle = self[variablePrefix + 'CircleXaxis'](rate); if(parseInt(newXaxisForCircle) > parseInt(maxXaxisForCircle)) { @@ -288,7 +298,7 @@ if(parseInt(newRadiusForCircle) > parseInt(maxRadiusForCircle)) { newRadiusForCircle = maxRadiusForCircle; } - + d3.selectAll(cssTarget) .transition() .duration(400) @@ -297,7 +307,7 @@ .attr("r", newRadiusForCircle) .style("fill", self[variablePrefix + 'ColorRange'](errorPercentage)); } - + /* private */ function updateSparkline(variablePrefix, cssTarget, newDataPoint) { var currentTimeMilliseconds = new Date().getTime(); var data = self[variablePrefix + cssTarget + '_data']; @@ -313,40 +323,40 @@ self[variablePrefix + cssTarget + '_data'] = data; } else { if(typeof newDataPoint == 'object') { - /* if an array is passed in we'll replace the cached one */ + /* if an array is passed in we'll replace the cached one */ data = newDataPoint; } else { // else we just add to the existing one data.push({"v":parseFloat(newDataPoint),"t":currentTimeMilliseconds}); } } - + while(data.length > 200) { // 400 should be plenty for the 2 minutes we have the scale set to below even with a very low update latency - // remove data so we don't keep increasing forever + // remove data so we don't keep increasing forever data.shift(); - } - + } + if(data.length == 1 && data[0].v == 0) { //console.log("we have a single 0 so skipping"); // don't show if we have a single 0 return; } - + if(data.length > 1 && data[0].v == 0 && data[1].v != 0) { //console.log("we have a leading 0 so removing it"); // get rid of a leading 0 if the following number is not a 0 data.shift(); - } - + } + var xScale = d3.time.scale().domain([new Date(currentTimeMilliseconds-(60*1000*2)), new Date(currentTimeMilliseconds)]).range([0, 140]); - + var yMin = d3.min(data, function(d) { return d.v; }); var yMax = d3.max(data, function(d) { return d.v; }); var yScale = d3.scale.linear().domain([yMin, yMax]).nice().range([60, 0]); // y goes DOWN, so 60 is the "lowest" - + sparkline = d3.svg.line() // assign the X function to plot our line as we wish - .x(function(d,i) { + .x(function(d,i) { // return the X coordinate where we want to plot this datapoint based on the time return xScale(new Date(d.t)); }) @@ -354,14 +364,14 @@ return yScale(d.v); }) .interpolate("basis"); - + d3.selectAll(cssTarget).attr("d", sparkline(data)); } - + /* private */ function deleteCircuit(circuitName) { $('#CIRCUIT_' + circuitName).remove(); } - + }; // public methods for sorting @@ -391,7 +401,7 @@ $('#' + this.containerId + ' div.monitor').tsort("p.name", {order: direction}); }; - + HystrixCommandMonitor.prototype.sortByError = function() { var direction = "desc"; if(this.sortedBy == 'error_desc') { @@ -404,7 +414,7 @@ this.sortedBy = 'error_' + direction; $('#' + this.containerId + ' div.monitor').tsort(".errorPercentage .value", {order: direction}); }; - + HystrixCommandMonitor.prototype.sortByErrorThenVolume = function() { var direction = "desc"; if(this.sortedBy == 'error_then_volume_desc') { @@ -412,7 +422,7 @@ } this.sortByErrorThenVolumeInDirection(direction); }; - + HystrixCommandMonitor.prototype.sortByErrorThenVolumeInDirection = function(direction) { this.sortedBy = 'error_then_volume_' + direction; $('#' + this.containerId + ' div.monitor').tsort({order: direction, attr: 'error_then_volume'}); @@ -484,7 +494,7 @@ } else if(this.sortedBy == 'error_then_volume_asc') { this.sortByErrorThenVolumeInDirection('asc'); } else if(this.sortedBy == 'error_then_volume_desc') { - this.sortByErrorThenVolumeInDirection('desc'); + this.sortByErrorThenVolumeInDirection('desc'); } else if(this.sortedBy == 'lat90_asc') { this.sortByMetricInDirection('asc', '.latency90 .value'); } else if(this.sortedBy == 'lat90_desc') { @@ -505,7 +515,7 @@ this.sortByMetricInDirection('asc', '.latencyMedian .value'); } else if(this.sortedBy == 'latMedian_desc') { this.sortByMetricInDirection('desc', '.latencyMedian .value'); - } + } }; // default sort type and direction diff --git a/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixCommand/templates/hystrixCircuitContainer.html b/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixCommand/templates/hystrixCircuitContainer.html index 4d19dbe0..91407c19 100644 --- a/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixCommand/templates/hystrixCircuitContainer.html +++ b/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixCommand/templates/hystrixCircuitContainer.html @@ -9,7 +9,7 @@ %>
-
+
<% if(includeDetailIcon) { %>

style="padding-right:16px"> <%= displayName %> diff --git a/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html b/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html index 6af0ef2c..035ae845 100644 --- a/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html +++ b/spring-cloud-netflix-hystrix-dashboard/src/main/resources/static/hystrix/components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html @@ -10,7 +10,7 @@ %>

-

><%= displayName %>

+

><%= displayName %>

diff --git a/spring-cloud-netflix-hystrix-dashboard/src/main/resources/templates/hystrix/monitor.ftl b/spring-cloud-netflix-hystrix-dashboard/src/main/resources/templates/hystrix/monitor.ftl index c54f6dc4..c4c8e3c6 100644 --- a/spring-cloud-netflix-hystrix-dashboard/src/main/resources/templates/hystrix/monitor.ftl +++ b/spring-cloud-netflix-hystrix-dashboard/src/main/resources/templates/hystrix/monitor.ftl @@ -61,8 +61,7 @@
Loading ...
-
- +