Make Hystrix dash embeddable
This commit is contained in:
@@ -6,7 +6,7 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.cloud.netflix.hystrix.HystrixDashboardConfiguration;
|
||||
import org.springframework.cloud.netflix.hystrix.dashboard.HystrixDashboardConfiguration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.springframework.cloud.netflix.hystrix;
|
||||
package org.springframework.cloud.netflix.hystrix.dashboard;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -0,0 +1,196 @@
|
||||
.dependencies .spacer {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding-top:4px;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
|
||||
.dependencies .last {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.dependencies span.loading {
|
||||
display: block;
|
||||
padding-top: 6%;
|
||||
padding-bottom: 6%;
|
||||
color: gray;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dependencies span.loading.failed {
|
||||
color: red;
|
||||
}
|
||||
|
||||
|
||||
.dependencies div.monitor {
|
||||
float: left;
|
||||
margin-right:5px;
|
||||
margin-top:5px;
|
||||
}
|
||||
|
||||
.dependencies div.monitor p.name {
|
||||
font-weight:bold;
|
||||
font-size: 10pt;
|
||||
text-align: right;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.dependencies div.monitor_data {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* override the HREF when we have specified it as a tooltip to not act like a link */
|
||||
.dependencies div.monitor_data a.tooltip {
|
||||
text-decoration: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.dependencies div.monitor_data div.counters {
|
||||
text-align: right;
|
||||
padding-bottom: 10px;
|
||||
font-size: 10pt;
|
||||
clear: both;
|
||||
|
||||
}
|
||||
|
||||
.dependencies div.monitor_data div.counters div.cell {
|
||||
display: inline;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.dependencies .borderRight {
|
||||
border-right: 1px solid grey;
|
||||
padding-right: 6px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.dependencies div.cell .line {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dependencies div.monitor_data a,
|
||||
.dependencies span.rate_value {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
|
||||
.dependencies span.smaller {
|
||||
font-size: 8pt;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.dependencies div.tableRow {
|
||||
width:100%;
|
||||
white-space: nowrap;
|
||||
font-size: 8pt;
|
||||
margin: 0 auto;
|
||||
clear:both;
|
||||
padding-left:26%;
|
||||
}
|
||||
|
||||
.dependencies div.tableRow .cell {
|
||||
float:left;
|
||||
}
|
||||
|
||||
.dependencies div.tableRow .header {
|
||||
width:18%;
|
||||
text-align:right;
|
||||
padding-right:2%;
|
||||
}
|
||||
|
||||
.dependencies div.tableRow .data {
|
||||
width:17%;
|
||||
font-weight: bold;
|
||||
text-align:right;
|
||||
}
|
||||
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.dependencies .success {
|
||||
color: green;
|
||||
}
|
||||
.dependencies .shortCircuited {
|
||||
color: blue;
|
||||
}
|
||||
.dependencies .timeout {
|
||||
color: #FF9900; /* shade of orange */
|
||||
}
|
||||
.dependencies .failure {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.dependencies .rejected {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
.dependencies .exceptionsThrown {
|
||||
color: brown;
|
||||
}
|
||||
|
||||
.dependencies div.monitor_data a.rate {
|
||||
color: black;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
.dependencies div.rate {
|
||||
padding-top: 1px;
|
||||
clear:both;
|
||||
text-align:right;
|
||||
}
|
||||
|
||||
.dependencies .errorPercentage {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.dependencies div.cell .errorPercentage {
|
||||
padding-left:5px;
|
||||
font-size: 12pt !important;
|
||||
}
|
||||
|
||||
|
||||
.dependencies div.monitor div.chart {
|
||||
}
|
||||
|
||||
.dependencies div.monitor div.chart svg {
|
||||
}
|
||||
|
||||
.dependencies div.monitor div.chart svg text {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
|
||||
.dependencies div.circuitStatus {
|
||||
width:100%;
|
||||
white-space: nowrap;
|
||||
font-size: 9pt;
|
||||
margin: 0 auto;
|
||||
clear:both;
|
||||
text-align:right;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.dependencies #hidden {
|
||||
width:1px;
|
||||
height:1px;
|
||||
background: lightgrey;
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* sparkline */
|
||||
.dependencies path {
|
||||
stroke: steelblue;
|
||||
stroke-width: 1;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,536 @@
|
||||
|
||||
(function(window) {
|
||||
|
||||
// cache the templates we use on this page as global variables (asynchronously)
|
||||
jQuery.get(getRelativePath("../components/hystrixCommand/templates/hystrixCircuit.html"), function(data) {
|
||||
hystrixTemplateCircuit = data;
|
||||
});
|
||||
jQuery.get(getRelativePath("../components/hystrixCommand/templates/hystrixCircuitContainer.html"), function(data) {
|
||||
hystrixTemplateCircuitContainer = data;
|
||||
});
|
||||
|
||||
function getRelativePath(path) {
|
||||
var p = location.pathname.slice(0, location.pathname.lastIndexOf("/")+1);
|
||||
return p + path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
// intialize various variables we use for visualization
|
||||
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]);
|
||||
self.circuitCircleXaxis = d3.scale.linear().domain([0, 400]).range(["30%", maxYaxisForCircle]);
|
||||
self.circuitColorRange = d3.scale.linear().domain([10, 25, 40, 50]).range(["green", "#FFCC00", "#FF9900", "red"]);
|
||||
self.circuitErrorPercentageColorRange = d3.scale.linear().domain([0, 10, 35, 50]).range(["grey", "black", "#FF9900", "red"]);
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
// sort since we have added a new one
|
||||
self.sortSameAsLast();
|
||||
}, 10000);
|
||||
|
||||
|
||||
/**
|
||||
* END of Initialization on construction
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event listener to handle new messages from EventSource as streamed from the server.
|
||||
*/
|
||||
/* public */ self.eventSourceMessageListener = function(e) {
|
||||
var data = JSON.parse(e.data);
|
||||
if(data) {
|
||||
// check for reportingHosts (if not there, set it to 1 for singleHost vs cluster)
|
||||
if(!data.reportingHosts) {
|
||||
data.reportingHosts = 1;
|
||||
}
|
||||
|
||||
if(data && data.type == 'HystrixCommand') {
|
||||
if (data.deleteData == 'true') {
|
||||
deleteCircuit(data.escapedName);
|
||||
} else {
|
||||
displayCircuit(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Pre process the data before displying in the UI.
|
||||
* e.g Get Averages from sums, do rate calculation etc.
|
||||
*/
|
||||
function preProcessData(data) {
|
||||
validateData(data);
|
||||
// escape string used in jQuery & d3 selectors
|
||||
data.escapedName = data.name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\$1');
|
||||
// do math
|
||||
converAllAvg(data);
|
||||
calcRatePerSecond(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* We want to do this on any numerical values where we want per instance rather than cluster-wide sum.
|
||||
*/
|
||||
function converAllAvg(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);
|
||||
} else {
|
||||
data[key] = getInstanceAverage(data[key], data["reportingHosts"], decimal);
|
||||
}
|
||||
}
|
||||
|
||||
function getInstanceAverage(value, reportingHosts, decimal) {
|
||||
if (decimal) {
|
||||
return roundNumber(value/reportingHosts);
|
||||
} else {
|
||||
return Math.floor(value/reportingHosts);
|
||||
}
|
||||
}
|
||||
|
||||
function calcRatePerSecond(data) {
|
||||
var numberSeconds = data["propertyValue_metricsRollingStatisticalWindowInMilliseconds"] / 1000;
|
||||
|
||||
var totalRequests = data["requestCount"];
|
||||
if (totalRequests < 0) {
|
||||
totalRequests = 0;
|
||||
}
|
||||
data["ratePerSecond"] = roundNumber(totalRequests / numberSeconds);
|
||||
data["ratePerSecondPerHost"] = roundNumber(totalRequests / numberSeconds / data["reportingHosts"]) ;
|
||||
}
|
||||
|
||||
function validateData(data) {
|
||||
assertNotNull(data,"reportingHosts");
|
||||
assertNotNull(data,"type");
|
||||
assertNotNull(data,"name");
|
||||
assertNotNull(data,"group");
|
||||
// assertNotNull(data,"currentTime");
|
||||
assertNotNull(data,"isCircuitBreakerOpen");
|
||||
assertNotNull(data,"errorPercentage");
|
||||
assertNotNull(data,"errorCount");
|
||||
assertNotNull(data,"requestCount");
|
||||
assertNotNull(data,"rollingCountCollapsedRequests");
|
||||
assertNotNull(data,"rollingCountExceptionsThrown");
|
||||
assertNotNull(data,"rollingCountFailure");
|
||||
assertNotNull(data,"rollingCountFallbackFailure");
|
||||
assertNotNull(data,"rollingCountFallbackRejection");
|
||||
assertNotNull(data,"rollingCountFallbackSuccess");
|
||||
assertNotNull(data,"rollingCountResponsesFromCache");
|
||||
assertNotNull(data,"rollingCountSemaphoreRejected");
|
||||
assertNotNull(data,"rollingCountShortCircuited");
|
||||
assertNotNull(data,"rollingCountSuccess");
|
||||
assertNotNull(data,"rollingCountThreadPoolRejected");
|
||||
assertNotNull(data,"rollingCountTimeout");
|
||||
assertNotNull(data,"currentConcurrentExecutionCount");
|
||||
assertNotNull(data,"latencyExecute_mean");
|
||||
assertNotNull(data,"latencyExecute");
|
||||
assertNotNull(data,"latencyTotal_mean");
|
||||
assertNotNull(data,"latencyTotal");
|
||||
assertNotNull(data,"propertyValue_circuitBreakerRequestVolumeThreshold");
|
||||
assertNotNull(data,"propertyValue_circuitBreakerSleepWindowInMilliseconds");
|
||||
assertNotNull(data,"propertyValue_circuitBreakerErrorThresholdPercentage");
|
||||
assertNotNull(data,"propertyValue_circuitBreakerForceOpen");
|
||||
assertNotNull(data,"propertyValue_executionIsolationStrategy");
|
||||
assertNotNull(data,"propertyValue_executionIsolationThreadTimeoutInMilliseconds");
|
||||
assertNotNull(data,"propertyValue_executionIsolationThreadInterruptOnTimeout");
|
||||
// assertNotNull(data,"propertyValue_executionIsolationThreadPoolKeyOverride");
|
||||
assertNotNull(data,"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests");
|
||||
assertNotNull(data,"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests");
|
||||
assertNotNull(data,"propertyValue_requestCacheEnabled");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// args for display
|
||||
if(self.args.includeDetailIcon != undefined && self.args.includeDetailIcon) {
|
||||
data.includeDetailIcon = true;
|
||||
}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);
|
||||
} else {
|
||||
updateSparkline('circuit', '#CIRCUIT_' + data.escapedName + ' path', ratePerSecond);
|
||||
}
|
||||
|
||||
if(addNew) {
|
||||
// sort since we added a new circuit
|
||||
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;
|
||||
var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
|
||||
var resultAsString = result.toString();
|
||||
if(resultAsString.indexOf('.') == -1) {
|
||||
resultAsString = resultAsString + '.0';
|
||||
}
|
||||
return resultAsString;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* private */ function updateCircle(variablePrefix, cssTarget, rate, errorPercentage) {
|
||||
var newXaxisForCircle = self[variablePrefix + 'CircleXaxis'](rate);
|
||||
if(parseInt(newXaxisForCircle) > parseInt(maxXaxisForCircle)) {
|
||||
newXaxisForCircle = maxXaxisForCircle;
|
||||
}
|
||||
var newYaxisForCircle = self[variablePrefix + 'CircleYaxis'](rate);
|
||||
if(parseInt(newYaxisForCircle) > parseInt(maxYaxisForCircle)) {
|
||||
newYaxisForCircle = maxYaxisForCircle;
|
||||
}
|
||||
var newRadiusForCircle = self[variablePrefix + 'CircleRadius'](rate);
|
||||
if(parseInt(newRadiusForCircle) > parseInt(maxRadiusForCircle)) {
|
||||
newRadiusForCircle = maxRadiusForCircle;
|
||||
}
|
||||
|
||||
d3.selectAll(cssTarget)
|
||||
.transition()
|
||||
.duration(400)
|
||||
.attr("cy", newYaxisForCircle)
|
||||
.attr("cx", newXaxisForCircle)
|
||||
.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'];
|
||||
if(typeof data == 'undefined') {
|
||||
// else it's new
|
||||
if(typeof newDataPoint == 'object') {
|
||||
// we received an array of values, so initialize with it
|
||||
data = newDataPoint;
|
||||
} else {
|
||||
// v: VALUE, t: TIME_IN_MILLISECONDS
|
||||
data = [{"v":parseFloat(newDataPoint),"t":currentTimeMilliseconds}];
|
||||
}
|
||||
self[variablePrefix + cssTarget + '_data'] = data;
|
||||
} else {
|
||||
if(typeof newDataPoint == 'object') {
|
||||
/* 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
|
||||
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) {
|
||||
// return the X coordinate where we want to plot this datapoint based on the time
|
||||
return xScale(new Date(d.t));
|
||||
})
|
||||
.y(function(d) {
|
||||
return yScale(d.v);
|
||||
})
|
||||
.interpolate("basis");
|
||||
|
||||
d3.selectAll(cssTarget).attr("d", sparkline(data));
|
||||
}
|
||||
|
||||
/* private */ function deleteCircuit(circuitName) {
|
||||
$('#CIRCUIT_' + circuitName).remove();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// public methods for sorting
|
||||
HystrixCommandMonitor.prototype.sortByVolume = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'rate_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortByVolumeInDirection(direction);
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByVolumeInDirection = function(direction) {
|
||||
this.sortedBy = 'rate_' + direction;
|
||||
$('#' + this.containerId + ' div.monitor').tsort({order: direction, attr: 'rate_value'});
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortAlphabetically = function() {
|
||||
var direction = "asc";
|
||||
if(this.sortedBy == 'alph_asc') {
|
||||
direction = 'desc';
|
||||
}
|
||||
this.sortAlphabeticalInDirection(direction);
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortAlphabeticalInDirection = function(direction) {
|
||||
this.sortedBy = 'alph_' + direction;
|
||||
$('#' + this.containerId + ' div.monitor').tsort("p.name", {order: direction});
|
||||
};
|
||||
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByError = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'error_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortByErrorInDirection(direction);
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByErrorInDirection = function(direction) {
|
||||
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') {
|
||||
direction = 'asc';
|
||||
}
|
||||
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'});
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByLatency90 = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'lat90_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortedBy = 'lat90_' + direction;
|
||||
this.sortByMetricInDirection(direction, ".latency90 .value");
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByLatency99 = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'lat99_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortedBy = 'lat99_' + direction;
|
||||
this.sortByMetricInDirection(direction, ".latency99 .value");
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByLatency995 = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'lat995_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortedBy = 'lat995_' + direction;
|
||||
this.sortByMetricInDirection(direction, ".latency995 .value");
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByLatencyMean = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'latMean_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortedBy = 'latMean_' + direction;
|
||||
this.sortByMetricInDirection(direction, ".latencyMean .value");
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByLatencyMedian = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'latMedian_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortedBy = 'latMedian_' + direction;
|
||||
this.sortByMetricInDirection(direction, ".latencyMedian .value");
|
||||
};
|
||||
|
||||
HystrixCommandMonitor.prototype.sortByMetricInDirection = function(direction, metric) {
|
||||
$('#' + this.containerId + ' div.monitor').tsort(metric, {order: direction});
|
||||
};
|
||||
|
||||
// this method is for when new divs are added to cause the elements to be sorted to whatever the user last chose
|
||||
HystrixCommandMonitor.prototype.sortSameAsLast = function() {
|
||||
if(this.sortedBy == 'alph_asc') {
|
||||
this.sortAlphabeticalInDirection('asc');
|
||||
} else if(this.sortedBy == 'alph_desc') {
|
||||
this.sortAlphabeticalInDirection('desc');
|
||||
} else if(this.sortedBy == 'rate_asc') {
|
||||
this.sortByVolumeInDirection('asc');
|
||||
} else if(this.sortedBy == 'rate_desc') {
|
||||
this.sortByVolumeInDirection('desc');
|
||||
} else if(this.sortedBy == 'error_asc') {
|
||||
this.sortByErrorInDirection('asc');
|
||||
} else if(this.sortedBy == 'error_desc') {
|
||||
this.sortByErrorInDirection('desc');
|
||||
} else if(this.sortedBy == 'error_then_volume_asc') {
|
||||
this.sortByErrorThenVolumeInDirection('asc');
|
||||
} else if(this.sortedBy == 'error_then_volume_desc') {
|
||||
this.sortByErrorThenVolumeInDirection('desc');
|
||||
} else if(this.sortedBy == 'lat90_asc') {
|
||||
this.sortByMetricInDirection('asc', '.latency90 .value');
|
||||
} else if(this.sortedBy == 'lat90_desc') {
|
||||
this.sortByMetricInDirection('desc', '.latency90 .value');
|
||||
} else if(this.sortedBy == 'lat99_asc') {
|
||||
this.sortByMetricInDirection('asc', '.latency99 .value');
|
||||
} else if(this.sortedBy == 'lat99_desc') {
|
||||
this.sortByMetricInDirection('desc', '.latency99 .value');
|
||||
} else if(this.sortedBy == 'lat995_asc') {
|
||||
this.sortByMetricInDirection('asc', '.latency995 .value');
|
||||
} else if(this.sortedBy == 'lat995_desc') {
|
||||
this.sortByMetricInDirection('desc', '.latency995 .value');
|
||||
} else if(this.sortedBy == 'latMean_asc') {
|
||||
this.sortByMetricInDirection('asc', '.latencyMean .value');
|
||||
} else if(this.sortedBy == 'latMean_desc') {
|
||||
this.sortByMetricInDirection('desc', '.latencyMean .value');
|
||||
} else if(this.sortedBy == 'latMedian_asc') {
|
||||
this.sortByMetricInDirection('asc', '.latencyMedian .value');
|
||||
} else if(this.sortedBy == 'latMedian_desc') {
|
||||
this.sortByMetricInDirection('desc', '.latencyMedian .value');
|
||||
}
|
||||
};
|
||||
|
||||
// default sort type and direction
|
||||
this.sortedBy = 'alph_asc';
|
||||
|
||||
|
||||
// a temporary home for the logger until we become more sophisticated
|
||||
function log(message) {
|
||||
console.log(message);
|
||||
};
|
||||
|
||||
function addCommas(nStr){
|
||||
nStr += '';
|
||||
if(nStr.length <=3) {
|
||||
return nStr; //shortcut if we don't need commas
|
||||
}
|
||||
x = nStr.split('.');
|
||||
x1 = x[0];
|
||||
x2 = x.length > 1 ? '.' + x[1] : '';
|
||||
var rgx = /(\d+)(\d{3})/;
|
||||
while (rgx.test(x1)) {
|
||||
x1 = x1.replace(rgx, '$1' + ',' + '$2');
|
||||
}
|
||||
return x1 + x2;
|
||||
}
|
||||
})(window);
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 888 B |
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,73 @@
|
||||
<div class="counters">
|
||||
|
||||
<div class="cell line">
|
||||
<a href="javascript://" title="Error Percentage [Timed-out + Threadpool Rejected + Failure / Total]" class="tooltip errorPercentage"><span class="value"><%= errorPercentage %></span> %</a>
|
||||
</div>
|
||||
|
||||
<div class="cell borderRight">
|
||||
<% if(propertyValue_executionIsolationStrategy == 'THREAD') { %>
|
||||
<a href="javascript://" title="Timed-out Request Count" class="line tooltip timeout"><%= addCommas(rollingCountTimeout) %></a>
|
||||
<a href="javascript://" title="Threadpool Rejected Request Count" class="line tooltip rejected"><%= addCommas(rollingCountThreadPoolRejected) %></a>
|
||||
<% } %>
|
||||
<a href="javascript://" title="Failure Request Count" class="line tooltip failure"><%= addCommas(rollingCountFailure) %></a>
|
||||
</div>
|
||||
<div class="cell borderRight">
|
||||
<a href="javascript://" title="Successful Request Count" class="line tooltip success"><%= addCommas(rollingCountSuccess) %></a>
|
||||
<a href="javascript://" title="Short-circuited Request Count" class="line tooltip shortCircuited"><%= addCommas(rollingCountShortCircuited) %></a>
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rate">
|
||||
<a href="javascript://" title="Total Request Rate per Second per Reporting Host" class="tooltip rate"><span class="smaller">Host: </span><span class="ratePerSecondPerHost"><%= addCommas(roundNumber(ratePerSecondPerHost)) %></span>/s</a>
|
||||
</div>
|
||||
<div class="rate">
|
||||
<a href="javascript://" title="Total Request Rate per Second for Cluster" class="tooltip rate"><span class="smaller">Cluster: </span><span class="ratePerSecond"><%= addCommas(roundNumber(ratePerSecond)) %></span>/s</a>
|
||||
</div>
|
||||
|
||||
<div class="circuitStatus">
|
||||
<% if(propertyValue_circuitBreakerForceClosed) { %>
|
||||
<span class="smaller">[ <font color="orange">Forced Closed</font> ]</span>
|
||||
<% } %>
|
||||
<% if(propertyValue_circuitBreakerForceOpen) { %>
|
||||
Circuit <font color="red">Forced Open</font>
|
||||
<% } else { %>
|
||||
<% if(isCircuitBreakerOpen == reportingHosts) { %>
|
||||
Circuit <font color="red">Open</font>
|
||||
<% } else if(isCircuitBreakerOpen == 0) { %>
|
||||
Circuit <font color="green">Closed</font>
|
||||
<% } else {
|
||||
/* We have some circuits that are open */
|
||||
%>
|
||||
Circuit <font color="orange"><%= isCircuitBreakerOpen.replace("true", "Open").replace("false", "Closed") %>)</font>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="tableRow">
|
||||
<% if(typeof reportingHosts != 'undefined') { %>
|
||||
<div class="cell header">Hosts</div>
|
||||
<div class="cell data"><%= reportingHosts %></div>
|
||||
<% } else { %>
|
||||
<div class="cell header">Host</div>
|
||||
<div class="cell data">Single</div>
|
||||
<% } %>
|
||||
<div class="cell header">90th</div>
|
||||
<div class="cell data latency90"><span class="value"><%= getInstanceAverage(latencyExecute['90'], reportingHosts, false) %></span>ms</div>
|
||||
</div>
|
||||
<div class="tableRow">
|
||||
<div class="cell header">Median</div>
|
||||
<div class="cell data latencyMedian"><span class="value"><%= getInstanceAverage(latencyExecute['50'], reportingHosts, false) %></span>ms</div>
|
||||
<div class="cell header">99th</div>
|
||||
<div class="cell data latency99"><span class="value"><%= getInstanceAverage(latencyExecute['99'], reportingHosts, false) %></span>ms</div>
|
||||
</div>
|
||||
<div class="tableRow">
|
||||
<div class="cell header">Mean</div>
|
||||
<div class="cell data latencyMean"><span class="value"><%= latencyExecute_mean %></span>ms</div>
|
||||
<div class="cell header">99.5th</div>
|
||||
<div class="cell data latency995"><span class="value"><%= getInstanceAverage(latencyExecute['99.5'], reportingHosts, false) %></span>ms</div>
|
||||
</div>
|
||||
|
||||
<!-- <a href="">History</a> <a href="">EPIC</a> <a href="">Actions</a> -->
|
||||
@@ -0,0 +1,40 @@
|
||||
<div class="monitor" id="CIRCUIT_<%= name %>" style="position:relative;">
|
||||
<%
|
||||
var displayName = name;
|
||||
var toolTip = "";
|
||||
if(displayName.length > 32) {
|
||||
displayName = displayName.substring(0,4) + "..." + displayName.substring(displayName.length-20, displayName.length);
|
||||
toolTip = "title=\"" + name + "\"";
|
||||
}
|
||||
%>
|
||||
|
||||
<div id="chart_CIRCUIT_<%= name %>" class="chart" style="position:absolute;top:0px;left:0; float:left; width:100%; height:100%;"></div>
|
||||
<div style="position:absolute;top:0x;width:100%;height:15px;opacity:0.8; background:white;">
|
||||
<% if(includeDetailIcon) { %>
|
||||
<p class="name" <%= toolTip %> style="padding-right:16px">
|
||||
<%= displayName %>
|
||||
<a href="../dependencies/command.jsp?name=<%= name %>"><img src="../components/hystrixCommand/magnifying-glass-icon-20.png" height="14px" width="14px" border="0" style="position: absolute; right:0px;"></a>
|
||||
</p>
|
||||
<% } else { %>
|
||||
<p class="name" <%= toolTip %>><%= displayName %></p>
|
||||
<% } %>
|
||||
</div>
|
||||
<div style="position:absolute;top:15px;; opacity:0.8; background:white; width:100%; height:95%;">
|
||||
<div class="monitor_data"></div>
|
||||
</div>
|
||||
<div id="graph_CIRCUIT_<%= name %>" class="graph" style="position:absolute;top:25px;left:0; float:left; width:140px; height:62px;"></div>
|
||||
|
||||
<script>
|
||||
var y = 200;
|
||||
/* escape with two backslashes */
|
||||
var vis = d3.select("#chart_CIRCUIT_<%= name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\\\$1') %>").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 */
|
||||
circle.style("fill", "green").attr("cx", "30%").attr("cy", "30%").attr("r", 5);
|
||||
|
||||
/* add the line graph - it will be populated by javascript, no default to show here */
|
||||
/* escape with two backslashes */
|
||||
var graph = d3.select("#graph_CIRCUIT_<%= name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\\\$1') %>").append("svg:svg").attr("width", "100%").attr("height", "100%");
|
||||
</script>
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
<div class="tableRow">
|
||||
<div class="cell header">Median</div>
|
||||
<div class="cell data"><span class="value"><%= sla_medianLastMinute %></span>ms</div>
|
||||
<div class="cell header">99th</div>
|
||||
<div class="cell data"><span class="value"><%= sla_percentile99LastMinute %></span>ms</div>
|
||||
</div>
|
||||
@@ -0,0 +1,141 @@
|
||||
.dependencyThreadPools .spacer {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding-top:4px;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
|
||||
.dependencyThreadPools .last {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools span.loading {
|
||||
display: block;
|
||||
padding-top: 6%;
|
||||
padding-bottom: 6%;
|
||||
color: gray;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dependencyThreadPools span.loading.failed {
|
||||
color: red;
|
||||
}
|
||||
|
||||
|
||||
.dependencyThreadPools div.monitor {
|
||||
float: left;
|
||||
margin-right:5px; /* these are tweaked to look good on desktop and iPad portrait, and fit things densely */
|
||||
margin-top:5px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.monitor p.name {
|
||||
font-weight:bold;
|
||||
font-size: 10pt;
|
||||
text-align: right;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.monitor_data {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.dependencyThreadPools span.smaller {
|
||||
font-size: 8pt;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
|
||||
.dependencyThreadPools div.tableRow {
|
||||
width:100%;
|
||||
white-space: nowrap;
|
||||
font-size: 8pt;
|
||||
margin: 0 auto;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.tableRow .cell {
|
||||
float:left;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.tableRow .header {
|
||||
text-align:right;
|
||||
padding-right:5px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.tableRow .header.left {
|
||||
width:85px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.tableRow .header.right {
|
||||
width:75px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.tableRow .data {
|
||||
font-weight: bold;
|
||||
text-align:right;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.tableRow .data.left {
|
||||
width:30px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.tableRow .data.right {
|
||||
width:45px;
|
||||
}
|
||||
|
||||
.dependencyThreadPools 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: 110px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* override the HREF when we have specified it as a tooltip to not act like a link */
|
||||
.dependencyThreadPools div.monitor_data a.tooltip {
|
||||
text-decoration: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.monitor_data a.rate {
|
||||
font-weight:bold;
|
||||
color: black;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.rate {
|
||||
padding-top: 1px;
|
||||
clear:both;
|
||||
text-align:right;
|
||||
}
|
||||
|
||||
.dependencyThreadPools span.rate_value {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.dependencyThreadPools div.monitor div.chart {
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.monitor div.chart svg {
|
||||
}
|
||||
|
||||
.dependencyThreadPools div.monitor div.chart svg text {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
.dependencyThreadPools #hidden {
|
||||
width:1px;
|
||||
height:1px;
|
||||
background: lightgrey;
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
|
||||
(function(window) {
|
||||
|
||||
// cache the templates we use on this page as global variables (asynchronously)
|
||||
jQuery.get(getRelativePath("../components/hystrixThreadPool/templates/hystrixThreadPool.html"), function(data) {
|
||||
htmlTemplate = data;
|
||||
});
|
||||
jQuery.get(getRelativePath("../components/hystrixThreadPool/templates/hystrixThreadPoolContainer.html"), function(data) {
|
||||
htmlTemplateContainer = data;
|
||||
});
|
||||
|
||||
function getRelativePath(path) {
|
||||
var p = location.pathname.slice(0, location.pathname.lastIndexOf("/")+1);
|
||||
return p + path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object containing functions for displaying and updating the UI with streaming data.
|
||||
*
|
||||
* Publish this externally as "HystrixThreadPoolMonitor"
|
||||
*/
|
||||
window.HystrixThreadPoolMonitor = function(containerId) {
|
||||
|
||||
var self = this; // keep scope under control
|
||||
|
||||
this.containerId = containerId;
|
||||
|
||||
/**
|
||||
* Initialization on construction
|
||||
*/
|
||||
// intialize various variables we use for visualization
|
||||
var maxXaxisForCircle="40%";
|
||||
var maxYaxisForCircle="40%";
|
||||
var maxRadiusForCircle="125";
|
||||
var maxDomain = 2000;
|
||||
|
||||
self.circleRadius = d3.scale.pow().exponent(0.5).domain([0, maxDomain]).range(["5", maxRadiusForCircle]); // requests per second per host
|
||||
self.circleYaxis = d3.scale.linear().domain([0, maxDomain]).range(["30%", maxXaxisForCircle]);
|
||||
self.circleXaxis = d3.scale.linear().domain([0, maxDomain]).range(["30%", maxYaxisForCircle]);
|
||||
self.colorRange = d3.scale.linear().domain([10, 25, 40, 50]).range(["green", "#FFCC00", "#FF9900", "red"]);
|
||||
self.errorPercentageColorRange = d3.scale.linear().domain([0, 10, 35, 50]).range(["grey", "black", "#FF9900", "red"]);
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
// sort since we have added a new one
|
||||
self.sortSameAsLast();
|
||||
}, 1000)
|
||||
|
||||
/**
|
||||
* END of Initialization on construction
|
||||
*/
|
||||
|
||||
/**
|
||||
* Event listener to handle new messages from EventSource as streamed from the server.
|
||||
*/
|
||||
/* public */ self.eventSourceMessageListener = function(e) {
|
||||
var data = JSON.parse(e.data);
|
||||
if(data) {
|
||||
// check for reportingHosts (if not there, set it to 1 for singleHost vs cluster)
|
||||
if(!data.reportingHosts) {
|
||||
data.reportingHosts = 1;
|
||||
}
|
||||
|
||||
if(data && data.type == 'HystrixThreadPool') {
|
||||
if (data.deleteData == 'true') {
|
||||
deleteThreadPool(data.escapedName);
|
||||
} else {
|
||||
displayThreadPool(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre process the data before displying in the UI.
|
||||
* e.g Get Averages from sums, do rate calculation etc.
|
||||
*/
|
||||
function preProcessData(data) {
|
||||
validateData(data);
|
||||
// escape string used in jQuery & d3 selectors
|
||||
data.escapedName = data.name.replace(/([ !"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g,'\\$1');
|
||||
// do math
|
||||
converAllAvg(data);
|
||||
calcRatePerSecond(data);
|
||||
}
|
||||
|
||||
function converAllAvg(data) {
|
||||
convertAvg(data, "propertyValue_queueSizeRejectionThreshold", 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] = roundNumber(data[key]/data["reportingHosts"]);
|
||||
} else {
|
||||
data[key] = Math.floor(data[key]/data["reportingHosts"]);
|
||||
}
|
||||
}
|
||||
|
||||
function calcRatePerSecond(data) {
|
||||
var numberSeconds = data["propertyValue_metricsRollingStatisticalWindowInMilliseconds"] / 1000;
|
||||
|
||||
var totalThreadsExecuted = data["rollingCountThreadsExecuted"];
|
||||
if (totalThreadsExecuted < 0) {
|
||||
totalThreadsExecuted = 0;
|
||||
}
|
||||
data["ratePerSecond"] = roundNumber(totalThreadsExecuted / numberSeconds);
|
||||
data["ratePerSecondPerHost"] = roundNumber(totalThreadsExecuted / numberSeconds / data["reportingHosts"]);
|
||||
}
|
||||
|
||||
function validateData(data) {
|
||||
|
||||
assertNotNull(data,"type");
|
||||
assertNotNull(data,"name");
|
||||
// assertNotNull(data,"currentTime");
|
||||
assertNotNull(data,"currentActiveCount");
|
||||
assertNotNull(data,"currentCompletedTaskCount");
|
||||
assertNotNull(data,"currentCorePoolSize");
|
||||
assertNotNull(data,"currentLargestPoolSize");
|
||||
assertNotNull(data,"currentMaximumPoolSize");
|
||||
assertNotNull(data,"currentPoolSize");
|
||||
assertNotNull(data,"currentQueueSize");
|
||||
assertNotNull(data,"currentTaskCount");
|
||||
assertNotNull(data,"rollingCountThreadsExecuted");
|
||||
assertNotNull(data,"rollingMaxActiveThreads");
|
||||
assertNotNull(data,"reportingHosts");
|
||||
|
||||
assertNotNull(data,"propertyValue_queueSizeRejectionThreshold");
|
||||
assertNotNull(data,"propertyValue_metricsRollingStatisticalWindowInMilliseconds");
|
||||
}
|
||||
|
||||
function assertNotNull(data, key) {
|
||||
if(data[key] == undefined) {
|
||||
if (key == "dependencyOwner") {
|
||||
data["dependencyOwner"] = data.name;
|
||||
} else {
|
||||
throw new Error("Key Missing: " + key + " for " + data.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to display the THREAD_POOL data
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
/* private */ function displayThreadPool(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;
|
||||
|
||||
var addNew = false;
|
||||
// check if we need to create the container
|
||||
if(!$('#THREAD_POOL_' + data.escapedName).length) {
|
||||
// it doesn't exist so add it
|
||||
var html = tmpl(htmlTemplateContainer, data);
|
||||
// remove the loading thing first
|
||||
$('#' + containerId + ' span.loading').remove();
|
||||
// get the current last column and remove the 'last' class from it
|
||||
$('#' + containerId + ' div.last').removeClass('last');
|
||||
// now create the new data and add it
|
||||
$('#' + containerId + '').append(html);
|
||||
// add the 'last' class to the column we just added
|
||||
$('#' + containerId + ' div.monitor').last().addClass('last');
|
||||
|
||||
// add the default sparkline graph
|
||||
d3.selectAll('#graph_THREAD_POOL_' + data.escapedName + ' svg').append("svg:path");
|
||||
|
||||
// remember this is new so we can trigger a sort after setting data
|
||||
addNew = true;
|
||||
}
|
||||
|
||||
// set the rate on the div element so it's available for sorting
|
||||
$('#THREAD_POOL_' + data.escapedName).attr('rate_value', data.ratePerSecondPerHost);
|
||||
|
||||
// now update/insert the data
|
||||
$('#THREAD_POOL_' + data.escapedName + ' div.monitor_data').html(tmpl(htmlTemplate, data));
|
||||
|
||||
// set variables for circle visualization
|
||||
var rate = data.ratePerSecondPerHost;
|
||||
// we will treat each item in queue as 1% of an error visualization
|
||||
// ie. 5 threads in queue per instance == 5% error percentage
|
||||
var errorPercentage = data.currentQueueSize / data.reportingHosts;
|
||||
|
||||
updateCircle('#THREAD_POOL_' + data.escapedName + ' circle', rate, errorPercentage);
|
||||
|
||||
if(addNew) {
|
||||
// sort since we added a new circuit
|
||||
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; // we are hardcoding to support only 1 decimal so that our padding logic at the end is simple
|
||||
var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
|
||||
var resultAsString = result.toString();
|
||||
if(resultAsString.indexOf('.') == -1) {
|
||||
resultAsString = resultAsString + '.';
|
||||
for(var i=0; i<dec; i++) {
|
||||
resultAsString = resultAsString + '0';
|
||||
}
|
||||
}
|
||||
return resultAsString;
|
||||
};
|
||||
|
||||
|
||||
/* private */ function updateCircle(cssTarget, rate, errorPercentage) {
|
||||
var newXaxisForCircle = self.circleXaxis(rate);
|
||||
if(parseInt(newXaxisForCircle) > parseInt(maxXaxisForCircle)) {
|
||||
newXaxisForCircle = maxXaxisForCircle;
|
||||
}
|
||||
var newYaxisForCircle = self.circleYaxis(rate);
|
||||
if(parseInt(newYaxisForCircle) > parseInt(maxYaxisForCircle)) {
|
||||
newYaxisForCircle = maxYaxisForCircle;
|
||||
}
|
||||
var newRadiusForCircle = self.circleRadius(rate);
|
||||
if(parseInt(newRadiusForCircle) > parseInt(maxRadiusForCircle)) {
|
||||
newRadiusForCircle = maxRadiusForCircle;
|
||||
}
|
||||
|
||||
d3.selectAll(cssTarget)
|
||||
.transition()
|
||||
.duration(400)
|
||||
.attr("cy", newYaxisForCircle)
|
||||
.attr("cx", newXaxisForCircle)
|
||||
.attr("r", newRadiusForCircle)
|
||||
.style("fill", self.colorRange(errorPercentage));
|
||||
}
|
||||
|
||||
/* private */ function deleteThreadPool(poolName) {
|
||||
$('#THREAD_POOL_' + poolName).remove();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// public methods for sorting
|
||||
HystrixThreadPoolMonitor.prototype.sortByVolume = function() {
|
||||
var direction = "desc";
|
||||
if(this.sortedBy == 'rate_desc') {
|
||||
direction = 'asc';
|
||||
}
|
||||
this.sortByVolumeInDirection(direction);
|
||||
}
|
||||
|
||||
HystrixThreadPoolMonitor.prototype.sortByVolumeInDirection = function(direction) {
|
||||
this.sortedBy = 'rate_' + direction;
|
||||
$('#' + this.containerId + ' div.monitor').tsort({order: direction, attr: 'rate_value'});
|
||||
}
|
||||
|
||||
HystrixThreadPoolMonitor.prototype.sortAlphabetically = function() {
|
||||
var direction = "asc";
|
||||
if(this.sortedBy == 'alph_asc') {
|
||||
direction = 'desc';
|
||||
}
|
||||
this.sortAlphabeticalInDirection(direction);
|
||||
}
|
||||
|
||||
HystrixThreadPoolMonitor.prototype.sortAlphabeticalInDirection = function(direction) {
|
||||
this.sortedBy = 'alph_' + direction;
|
||||
$('#' + this.containerId + ' div.monitor').tsort("p.name", {order: direction});
|
||||
}
|
||||
|
||||
HystrixThreadPoolMonitor.prototype.sortByMetricInDirection = function(direction, metric) {
|
||||
$('#' + this.containerId + ' div.monitor').tsort(metric, {order: direction});
|
||||
}
|
||||
|
||||
// this method is for when new divs are added to cause the elements to be sorted to whatever the user last chose
|
||||
HystrixThreadPoolMonitor.prototype.sortSameAsLast = function() {
|
||||
if(this.sortedBy == 'alph_asc') {
|
||||
this.sortAlphabeticalInDirection('asc');
|
||||
} else if(this.sortedBy == 'alph_desc') {
|
||||
this.sortAlphabeticalInDirection('desc');
|
||||
} else if(this.sortedBy == 'rate_asc') {
|
||||
this.sortByVolumeInDirection('asc');
|
||||
} else if(this.sortedBy == 'rate_desc') {
|
||||
this.sortByVolumeInDirection('desc');
|
||||
} else if(this.sortedBy == 'error_asc') {
|
||||
this.sortByErrorInDirection('asc');
|
||||
} else if(this.sortedBy == 'error_desc') {
|
||||
this.sortByErrorInDirection('desc');
|
||||
} else if(this.sortedBy == 'lat90_asc') {
|
||||
this.sortByMetricInDirection('asc', 'p90');
|
||||
} else if(this.sortedBy == 'lat90_desc') {
|
||||
this.sortByMetricInDirection('desc', 'p90');
|
||||
} else if(this.sortedBy == 'lat99_asc') {
|
||||
this.sortByMetricInDirection('asc', 'p99');
|
||||
} else if(this.sortedBy == 'lat99_desc') {
|
||||
this.sortByMetricInDirection('desc', 'p99');
|
||||
} else if(this.sortedBy == 'lat995_asc') {
|
||||
this.sortByMetricInDirection('asc', 'p995');
|
||||
} else if(this.sortedBy == 'lat995_desc') {
|
||||
this.sortByMetricInDirection('desc', 'p995');
|
||||
} else if(this.sortedBy == 'latMean_asc') {
|
||||
this.sortByMetricInDirection('asc', 'pMean');
|
||||
} else if(this.sortedBy == 'latMean_desc') {
|
||||
this.sortByMetricInDirection('desc', 'pMean');
|
||||
} else if(this.sortedBy == 'latMedian_asc') {
|
||||
this.sortByMetricInDirection('asc', 'pMedian');
|
||||
} else if(this.sortedBy == 'latMedian_desc') {
|
||||
this.sortByMetricInDirection('desc', 'pMedian');
|
||||
}
|
||||
}
|
||||
|
||||
// default sort type and direction
|
||||
this.sortedBy = 'alph_asc';
|
||||
|
||||
|
||||
// a temporary home for the logger until we become more sophisticated
|
||||
function log(message) {
|
||||
console.log(message);
|
||||
};
|
||||
|
||||
function addCommas(nStr){
|
||||
nStr += '';
|
||||
if(nStr.length <=3) {
|
||||
return nStr; //shortcut if we don't need commas
|
||||
}
|
||||
x = nStr.split('.');
|
||||
x1 = x[0];
|
||||
x2 = x.length > 1 ? '.' + x[1] : '';
|
||||
var rgx = /(\d+)(\d{3})/;
|
||||
while (rgx.test(x1)) {
|
||||
x1 = x1.replace(rgx, '$1' + ',' + '$2');
|
||||
}
|
||||
return x1 + x2;
|
||||
}
|
||||
})(window)
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="rate">
|
||||
<a href="javascript://" title="Total Execution Rate per Second per Reporting Host" class="tooltip rate"><span class="smaller">Host: </span><span class="ratePerSecondPerHost"><%= addCommas(ratePerSecondPerHost) %></span>/s</a>
|
||||
</div>
|
||||
<div class="rate">
|
||||
<a href="javascript://" title="Total Execution Rate per Second for Cluster" class="tooltip rate"><span class="smaller">Cluster: </span><span class="ratePerSecond"><%= addCommas(ratePerSecond) %></span>/s</a>
|
||||
</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="tableRow">
|
||||
<div class="cell header left">Active</div>
|
||||
<div class="cell data left"><%= currentActiveCount%></div>
|
||||
|
||||
<div class="cell header right">Max Active</div>
|
||||
<div class="cell data right"><%= addCommas(rollingMaxActiveThreads)%></div>
|
||||
</div>
|
||||
|
||||
<div class="tableRow">
|
||||
<div class="cell header left">Queued</div>
|
||||
<div class="cell data left"><%= currentQueueSize %></div>
|
||||
<div class="cell header right">Executions</div>
|
||||
<div class="cell data right"><%= addCommas(rollingCountThreadsExecuted)%></div>
|
||||
</div>
|
||||
<div class="tableRow">
|
||||
<div class="cell header left">Pool Size</div>
|
||||
<div class="cell data left"><%= currentPoolSize %></div>
|
||||
<div class="cell header right">Queue Size</div>
|
||||
<div class="cell data right"><%= propertyValue_queueSizeRejectionThreshold %></div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<div class="monitor" id="THREAD_POOL_<%= name %>" style="position:relative;">
|
||||
|
||||
<%
|
||||
var displayName = name;
|
||||
var toolTip = "";
|
||||
if(displayName.length > 32) {
|
||||
displayName = displayName.substring(0,4) + "..." + displayName.substring(displayName.length-20, displayName.length);
|
||||
toolTip = "title=\"" + name + "\"";
|
||||
}
|
||||
%>
|
||||
|
||||
<div id="chart_THREAD_POOL_<%= name %>" class="chart" style="position:absolute;top:0px;left:0; float:left; width:100%; height:100%;"></div>
|
||||
<div style="position:absolute;top:0x;width:100%;height:15px;opacity:0.8; background:white;"><p class="name" <%= toolTip %>><%= displayName %></p></div>
|
||||
<div style="position:absolute;top:15px;; opacity:0.8; background:white; width:100%; height:95%;">
|
||||
<div class="monitor_data"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
<% if(typeof errorPercentage != 'undefined') { %>
|
||||
var errorPercentageData = [<%= errorPercentage %>];
|
||||
<% } else { %>
|
||||
var errorPercentageData = [99];
|
||||
<% } %>
|
||||
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%");
|
||||
/* 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 */
|
||||
circle.style("fill", "green").attr("cx", "30%").attr("cy", "20%").attr("r", 5);
|
||||
</script>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,71 @@
|
||||
@IMPORT url("resets.css");
|
||||
|
||||
body {
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
}
|
||||
|
||||
img, object, embed {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
img {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
|
||||
#header {
|
||||
background: #FFFFFF url(http://raw.github.com/wiki/Netflix/Hystrix/images/hystrix-logo-tagline-tiny.png) no-repeat scroll 99% 0%;
|
||||
height: 65px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#header h2 {
|
||||
float:left;
|
||||
color: black;
|
||||
position:relative;
|
||||
padding-left: 20px;
|
||||
top: 26px;
|
||||
font-size: 20px;
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
}
|
||||
|
||||
#header .header_nav {
|
||||
position:absolute;
|
||||
top:48px;
|
||||
right:15px;
|
||||
}
|
||||
|
||||
#header .header_links {
|
||||
float:left;
|
||||
color: lightgray;
|
||||
font-size: 18px;
|
||||
top: 3px;
|
||||
padding-left: 10px;
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
}
|
||||
|
||||
#header .header_links a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#header .header_clusters {
|
||||
float:left;
|
||||
position:relative;
|
||||
padding-left: 10px;
|
||||
top: -1px;
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (min-width: 1500px) {
|
||||
|
||||
#header .header_nav {
|
||||
top:13px;
|
||||
right:130px;
|
||||
}
|
||||
|
||||
#header {
|
||||
background: #FFFFFF url(http://raw.github.com/wiki/Netflix/Hystrix/images/hystrix-logo-tagline-tiny.png) no-repeat scroll 99% 50%;
|
||||
height: 65px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
html5doctor.com Reset Stylesheet
|
||||
v1.6.1
|
||||
Last Updated: 2010-09-17
|
||||
Author: Richard Clark - http://richclarkdesign.com
|
||||
Twitter: @rich_clark
|
||||
*/
|
||||
|
||||
html, body, div, span, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
abbr, address, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, samp,
|
||||
small, strong, sub, sup, var,
|
||||
b, i,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin:0;
|
||||
padding:0;
|
||||
border:0;
|
||||
outline:0;
|
||||
font-size:100%;
|
||||
vertical-align:baseline;
|
||||
background:transparent;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height:1;
|
||||
}
|
||||
|
||||
article,aside,details,figcaption,figure,
|
||||
footer,header,hgroup,menu,nav,section {
|
||||
display:block;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style:none;
|
||||
}
|
||||
|
||||
blockquote, q {
|
||||
quotes:none;
|
||||
}
|
||||
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content:'';
|
||||
content:none;
|
||||
}
|
||||
|
||||
a {
|
||||
margin:0;
|
||||
padding:0;
|
||||
font-size:100%;
|
||||
vertical-align:baseline;
|
||||
background:transparent;
|
||||
}
|
||||
|
||||
/* change colours to suit your needs */
|
||||
ins {
|
||||
background-color:#ff9;
|
||||
color:#000;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
/* change colours to suit your needs */
|
||||
mark {
|
||||
background-color:#ff9;
|
||||
color:#000;
|
||||
font-style:italic;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
del {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
abbr[title], dfn[title] {
|
||||
border-bottom:1px dotted;
|
||||
cursor:help;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse:collapse;
|
||||
border-spacing:0;
|
||||
}
|
||||
|
||||
/* change border colour to suit your needs */
|
||||
hr {
|
||||
display:block;
|
||||
height:1px;
|
||||
border:0;
|
||||
border-top:1px solid #cccccc;
|
||||
margin:1em 0;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
input, select {
|
||||
vertical-align:middle;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/* SimpleGrid - a fork of CSSGrid by Crowd Favorite (https://github.com/crowdfavorite/css-grid)
|
||||
* http://simplegrid.info
|
||||
* by Conor Muirhead (http://conor.cc) of Early LLC (http://earlymade.com)
|
||||
* License: http://creativecommons.org/licenses/MIT/ */
|
||||
|
||||
/* Containers */
|
||||
body { font-size: 1.125em; }
|
||||
.grid{ width:1206px; }
|
||||
|
||||
/* 6-Col Grid Sizes */
|
||||
.slot-0,.slot-1,.slot-2,.slot-3,.slot-4,.slot-5{ width:176px; } /* Sixths */
|
||||
.slot-0-1,.slot-1-2,.slot-2-3,.slot-3-4,.slot-4-5{ width:382px; } /* Thirds */
|
||||
.slot-0-1-2-3,.slot-1-2-3-4,.slot-2-3-4-5{ width:794px; } /* Two-Thirds */
|
||||
.slot-0-1-2-3-4,.slot-1-2-3-4-5{ width:1000px; } /* Five-Sixths */
|
||||
|
||||
/* 4-Col Grid Sizes */
|
||||
.slot-6,.slot-7,.slot-8,.slot-9{ width:279px; } /* Quarters */
|
||||
.slot-6-7-8,.slot-7-8-9{ width:897px; } /* Three-Quarters */
|
||||
|
||||
/* 6-Col/4-Col Shared Grid Sizes */
|
||||
.slot-0-1-2,.slot-1-2-3,.slot-2-3-4,.slot-3-4-5, .slot-6-7,.slot-7-8,.slot-8-9{ width:588px; } /* Halves */
|
||||
@@ -0,0 +1,33 @@
|
||||
/* SimpleGrid - a fork of CSSGrid by Crowd Favorite (https://github.com/crowdfavorite/css-grid)
|
||||
* http://simplegrid.info
|
||||
* by Conor Muirhead (http://conor.cc) of Early LLC (http://earlymade.com)
|
||||
* License: http://creativecommons.org/licenses/MIT/ */
|
||||
|
||||
/* Containers */
|
||||
body { font-size: 0.875em; padding: 0; }
|
||||
.grid{ margin:0 auto; padding: 0 10px; width:700px; }
|
||||
.row{ clear:left; }
|
||||
|
||||
/* Slots Setup */
|
||||
.slot-0,.slot-1,.slot-2,.slot-3,.slot-4,.slot-5,.slot-0-1,.slot-0-1-2,.slot-0-1-2-3,.slot-0-1-2-3-4,.slot-0-1-2-3-4-5,.slot-1-2,.slot-1-2-3,.slot-1-2-3-4,.slot-1-2-3-4-5,.slot-2-3,.slot-2-3-4,.slot-2-3-4-5,.slot-3-4,.slot-3-4-5,.slot-4-5,.slot-6,.slot-7,.slot-8,.slot-9,.slot-6-7,.slot-6-7-8,.slot-6-7-8-9,.slot-7-8,.slot-7-8-9,.slot-8-9{ display:inline; float:left; margin-left:20px; }
|
||||
|
||||
/* 6-Col Grid Sizes */
|
||||
.slot-0,.slot-1,.slot-2,.slot-3,.slot-4,.slot-5{ width:100px; } /* Sixths */
|
||||
.slot-0-1,.slot-1-2,.slot-2-3,.slot-3-4,.slot-4-5{ width:220px; } /* Thirds */
|
||||
.slot-0-1-2-3,.slot-1-2-3-4,.slot-2-3-4-5{ width:460px; } /* Two-Thirds */
|
||||
.slot-0-1-2-3-4,.slot-1-2-3-4-5{ width:580px; } /* Five-Sixths */
|
||||
|
||||
/* 4-Col Grid Sizes */
|
||||
.slot-6,.slot-7,.slot-8,.slot-9{ width:160px; } /* Quarters */
|
||||
.slot-6-7-8,.slot-7-8-9{ width:520px; } /* Three-Quarters */
|
||||
|
||||
/* 6-Col/4-Col Shared Grid Sizes */
|
||||
.slot-0-1-2,.slot-1-2-3,.slot-2-3-4,.slot-3-4-5, .slot-6-7,.slot-7-8,.slot-8-9{ width:340px; } /* Halves */
|
||||
.slot-0-1-2-3-4-5, .slot-6-7-8-9{ width: 100%; } /* Full-Width */
|
||||
|
||||
/* Zeroing Out Leftmost Slot Margins */
|
||||
.slot-0,.slot-0-1,.slot-0-1-2,.slot-0-1-2-3,.slot-0-1-2-3-4,.slot-0-1-2-3-4-5,.slot-6,.slot-6-7,.slot-6-7-8,.slot-6-7-8-9,.slot-1 .slot-1,.slot-1-2 .slot-1,.slot-1-2 .slot-1-2,.slot-1-2-3 .slot-1,.slot-1-2-3 .slot-1-2,.slot-1-2-3 .slot-1-2-3,.slot-1-2-3-4 .slot-1,.slot-1-2-3-4 .slot-1-2,.slot-1-2-3-4 .slot-1-2-3,.slot-1-2-3-4 .slot-1-2-3-4,.slot-1-2-3-4-5 .slot-1,.slot-1-2-3-4-5 .slot-1-2,.slot-1-2-3-4-5 .slot-1-2-3,.slot-1-2-3-4-5 .slot-1-2-3-4,.slot-1-2-3-4-5 .slot-1-2-3-4-5,.slot-2 .slot-2,.slot-2-3 .slot-2,.slot-2-3 .slot-2-3,.slot-2-3-4 .slot-2,.slot-2-3-4 .slot-2-3,.slot-2-3-4 .slot-2-3-4,.slot-2-3-4-5 .slot-2,.slot-2-3-4-5 .slot-2-3,.slot-2-3-4-5 .slot-2-3-4,.slot-2-3-4-5 .slot-2-3-4-5,.slot-3 .slot-3,.slot-3-4 .slot-3,.slot-3-4 .slot-3-4,.slot-3-4-5 .slot-3,.slot-3-4-5 .slot-3-4,.slot-3-4-5 .slot-3-4-5,.slot-4 .slot-4,.slot-4-5 .slot-4,.slot-4-5 .slot-4-5,.slot-5 .slot-5,.slot-7 .slot-7,.slot-7-8 .slot-7,.slot-7-8 .slot-7-8,.slot-7-8-9 .slot-7,.slot-7-8-9 .slot-7-8,.slot-7-8-9 .slot-7-8-9,.slot-8 .slot-8,.slot-8-9 .slot-8,.slot-8-9 .slot-8-9{ margin-left:0 !important; } /* Important is to avoid repeating this in larger screen css files */
|
||||
|
||||
/* Row Clearfix */
|
||||
.row:after{ visibility:hidden; display:block; font-size:0; content:" "; clear:both; height:0; }
|
||||
.row{ zoom:1; }
|
||||
@@ -0,0 +1,24 @@
|
||||
/* SimpleGrid - a fork of CSSGrid by Crowd Favorite (https://github.com/crowdfavorite/css-grid)
|
||||
* http://simplegrid.info
|
||||
* by Conor Muirhead (http://conor.cc) of Early LLC (http://earlymade.com)
|
||||
* License: http://creativecommons.org/licenses/MIT/ */
|
||||
|
||||
/* Containers */
|
||||
body { font-size: 100%; }
|
||||
.grid{ width:966px; }
|
||||
|
||||
/* Slots Setup */
|
||||
.slot-0,.slot-1,.slot-2,.slot-3,.slot-4,.slot-5,.slot-0-1,.slot-0-1-2,.slot-0-1-2-3,.slot-0-1-2-3-4,.slot-0-1-2-3-4-5,.slot-1-2,.slot-1-2-3,.slot-1-2-3-4,.slot-1-2-3-4-5,.slot-2-3,.slot-2-3-4,.slot-2-3-4-5,.slot-3-4,.slot-3-4-5,.slot-4-5,.slot-6,.slot-7,.slot-8,.slot-9,.slot-6-7,.slot-6-7-8,.slot-6-7-8-9,.slot-7-8,.slot-7-8-9,.slot-8-9{ display:inline; float:left; margin-left:30px; }
|
||||
|
||||
/* 6-Col Grid Sizes */
|
||||
.slot-0,.slot-1,.slot-2,.slot-3,.slot-4,.slot-5{ width:136px; } /* Sixths */
|
||||
.slot-0-1,.slot-1-2,.slot-2-3,.slot-3-4,.slot-4-5{ width:302px; } /* Thirds */
|
||||
.slot-0-1-2-3,.slot-1-2-3-4,.slot-2-3-4-5{ width:634px; } /* Two-Thirds */
|
||||
.slot-0-1-2-3-4,.slot-1-2-3-4-5{ width:800px; } /* Five-Sixths */
|
||||
|
||||
/* 4-Col Grid Sizes */
|
||||
.slot-6,.slot-7,.slot-8,.slot-9{ width:219px; } /* Quarters */
|
||||
.slot-6-7-8,.slot-7-8-9{ width:717px; } /* Three-Quarters */
|
||||
|
||||
/* 6-Col/4-Col Shared Grid Sizes */
|
||||
.slot-0-1-2,.slot-1-2-3,.slot-2-3-4,.slot-3-4-5, .slot-6-7,.slot-7-8,.slot-8-9{ width:468px; } /* Halves */
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2011 Crowd Favorite, Ltd.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1 @@
|
||||
http://simplegrid.info/
|
||||
@@ -0,0 +1,27 @@
|
||||
/* Extension of SimpleGrid by benjchristensen to allow percentage based sizing on very large displays
|
||||
*
|
||||
* SimpleGrid - a fork of CSSGrid by Crowd Favorite (https://github.com/crowdfavorite/css-grid)
|
||||
* http://simplegrid.info
|
||||
* by Conor Muirhead (http://conor.cc) of Early LLC (http://earlymade.com)
|
||||
* License: http://creativecommons.org/licenses/MIT/ */
|
||||
|
||||
/* Containers */
|
||||
body { font-size: 1.125em; }
|
||||
.grid{ width:100%; }
|
||||
|
||||
/* Slots Setup */
|
||||
.slot-0,.slot-1,.slot-2,.slot-3,.slot-4,.slot-5,.slot-0-1,.slot-0-1-2,.slot-0-1-2-3,.slot-0-1-2-3-4,.slot-0-1-2-3-4-5,.slot-1-2,.slot-1-2-3,.slot-1-2-3-4,.slot-1-2-3-4-5,.slot-2-3,.slot-2-3-4,.slot-2-3-4-5,.slot-3-4,.slot-3-4-5,.slot-4-5,.slot-6,.slot-7,.slot-8,.slot-9,.slot-6-7,.slot-6-7-8,.slot-6-7-8-9,.slot-7-8,.slot-7-8-9,.slot-8-9{ display:inline; float:left; margin-left:0px; }
|
||||
|
||||
|
||||
/* 6-Col Grid Sizes */
|
||||
.slot-0,.slot-1,.slot-2,.slot-3,.slot-4,.slot-5{ width:16.6%; } /* Sixths */
|
||||
.slot-0-1,.slot-1-2,.slot-2-3,.slot-3-4,.slot-4-5{ width:33.3%; } /* Thirds */
|
||||
.slot-0-1-2-3,.slot-1-2-3-4,.slot-2-3-4-5{ width:66.6%; } /* Two-Thirds */
|
||||
.slot-0-1-2-3-4,.slot-1-2-3-4-5{ width:83.3%; } /* Five-Sixths */
|
||||
|
||||
/* 4-Col Grid Sizes */
|
||||
.slot-6,.slot-7,.slot-8,.slot-9{ width:25%; } /* Quarters */
|
||||
.slot-6-7-8,.slot-7-8-9{ width:75%; } /* Three-Quarters */
|
||||
|
||||
/* 6-Col/4-Col Shared Grid Sizes */
|
||||
.slot-0-1-2,.slot-1-2-3,.slot-2-3-4,.slot-3-4-5, .slot-6-7,.slot-7-8,.slot-8-9{ width:50%; } /* Halves */
|
||||
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<title>Hystrix Dashboard</title>
|
||||
|
||||
<!-- Javascript to monitor and display -->
|
||||
<script src="/webjars/jquery/2.1.1/jquery.min.js" type="text/javascript"></script>
|
||||
|
||||
<script>
|
||||
function sendToMonitor() {
|
||||
|
||||
if($('#stream').val().length > 0) {
|
||||
var url = "/hystrix/monitor/monitor.html?stream=" + encodeURIComponent($('#stream').val()) + "";
|
||||
if($('#delay').val().length > 0) {
|
||||
url += "&delay=" + $('#delay').val();
|
||||
}
|
||||
if($('#title').val().length > 0) {
|
||||
url += "&title=" + encodeURIComponent($('#title').val());
|
||||
}
|
||||
location.href= url;
|
||||
} else {
|
||||
$('#message').html("The 'stream' value is required.");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div style="width:800px;margin:0 auto;">
|
||||
|
||||
<center>
|
||||
<img width="264" height="233" src="https://raw.github.com/wiki/Netflix/Hystrix/images/hystrix-logo.png">
|
||||
<br>
|
||||
<br>
|
||||
|
||||
<h2>Hystrix Dashboard</h2>
|
||||
<input id="stream" type="textfield" size="120" placeholder="http://hostname:port/turbine/turbine.stream"></input>
|
||||
<br><br>
|
||||
<i>Cluster via Turbine (default cluster):</i> http://turbine-hostname:port/turbine.stream
|
||||
<br>
|
||||
<i>Cluster via Turbine (custom cluster):</i> http://turbine-hostname:port/turbine.stream?cluster=[clusterName]
|
||||
<br>
|
||||
<i>Single Hystrix App:</i> http://hystrix-app:port/hystrix.stream
|
||||
<br><br>
|
||||
Delay: <input id="delay" type="textfield" size="10" placeholder="2000"></input>ms
|
||||
|
||||
Title: <input id="title" type="textfield" size="60" placeholder="Example Hystrix App"></input><br>
|
||||
<br>
|
||||
<button onclick="sendToMonitor()">Monitor Stream</button>
|
||||
<br><br>
|
||||
<div id="message" style="color:red"></div>
|
||||
|
||||
</center>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
12
spring-cloud-netflix-core/src/main/resources/static/hystrix/js/jquery.tinysort.min.js
vendored
Normal file
12
spring-cloud-netflix-core/src/main/resources/static/hystrix/js/jquery.tinysort.min.js
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* jQuery TinySort - A plugin to sort child nodes by (sub) contents or attributes.
|
||||
*
|
||||
* Version: 1.0.5
|
||||
*
|
||||
* Copyright (c) 2008-2011 Ron Valstar http://www.sjeiti.com/
|
||||
*
|
||||
* Dual licensed under the MIT and GPL licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
(function(b){b.tinysort={id:"TinySort",version:"1.0.5",copyright:"Copyright (c) 2008-2011 Ron Valstar",uri:"http://tinysort.sjeiti.com/",defaults:{order:"asc",attr:"",place:"start",returns:false,useVal:false}};b.fn.extend({tinysort:function(h,j){if(h&&typeof(h)!="string"){j=h;h=null}var e=b.extend({},b.tinysort.defaults,j);var p={};this.each(function(t){var v=(!h||h=="")?b(this):b(this).find(h);var u=e.order=="rand"?""+Math.random():(e.attr==""?(e.useVal?v.val():v.text()):v.attr(e.attr));var s=b(this).parent();if(!p[s]){p[s]={s:[],n:[]}}if(v.length>0){p[s].s.push({s:u,e:b(this),n:t})}else{p[s].n.push({e:b(this),n:t})}});for(var g in p){var d=p[g];d.s.sort(function k(t,s){var i=t.s.toLowerCase?t.s.toLowerCase():t.s;var u=s.s.toLowerCase?s.s.toLowerCase():s.s;if(c(t.s)&&c(s.s)){i=parseFloat(t.s);u=parseFloat(s.s)}return(e.order=="asc"?1:-1)*(i<u?-1:(i>u?1:0))})}var m=[];for(var g in p){var d=p[g];var n=[];var f=b(this).length;switch(e.place){case"first":b.each(d.s,function(s,t){f=Math.min(f,t.n)});break;case"org":b.each(d.s,function(s,t){n.push(t.n)});break;case"end":f=d.n.length;break;default:f=0}var q=[0,0];for(var l=0;l<b(this).length;l++){var o=l>=f&&l<f+d.s.length;if(a(n,l)){o=true}var r=(o?d.s:d.n)[q[o?0:1]].e;r.parent().append(r);if(o||!e.returns){m.push(r.get(0))}q[o?0:1]++}}return this.pushStack(m)}});function c(e){var d=/^\s*?[\+-]?(\d*\.?\d*?)\s*?$/.exec(e);return d&&d.length>0?d[1]:false}function a(e,f){var d=false;b.each(e,function(h,g){if(!d){d=g==f}});return d}b.fn.TinySort=b.fn.Tinysort=b.fn.tsort=b.fn.tinysort})(jQuery);
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
//Simple JavaScript Templating
|
||||
//John Resig - http://ejohn.org/ - MIT Licensed
|
||||
// http://ejohn.org/blog/javascript-micro-templating/
|
||||
(function(window, undefined) {
|
||||
var cache = {};
|
||||
|
||||
window.tmpl = function tmpl(str, data) {
|
||||
try {
|
||||
// Figure out if we're getting a template, or if we need to
|
||||
// load the template - and be sure to cache the result.
|
||||
var fn = !/\W/.test(str) ?
|
||||
cache[str] = cache[str] ||
|
||||
tmpl(document.getElementById(str).innerHTML) :
|
||||
|
||||
// Generate a reusable function that will serve as a template
|
||||
// generator (and which will be cached).
|
||||
new Function("obj",
|
||||
"var p=[],print=function(){p.push.apply(p,arguments);};" +
|
||||
|
||||
// Introduce the data as local variables using with(){}
|
||||
"with(obj){p.push('" +
|
||||
|
||||
// Convert the template into pure JavaScript
|
||||
str
|
||||
.replace(/[\r\t\n]/g, " ")
|
||||
.split("<%").join("\t")
|
||||
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
|
||||
.replace(/\t=(.*?)%>/g, "',$1,'")
|
||||
.split("\t").join("');")
|
||||
.split("%>").join("p.push('")
|
||||
.split("\r").join("\\'")
|
||||
+ "');}return p.join('');");
|
||||
|
||||
//console.log(fn);
|
||||
|
||||
// Provide some basic currying to the user
|
||||
return data ? fn(data) : fn;
|
||||
}catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
})(window);
|
||||
@@ -0,0 +1,101 @@
|
||||
.container {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.row {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding-top:4px;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
|
||||
.last {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.menubar {
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
|
||||
.menubar div {
|
||||
padding-bottom:5px;
|
||||
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
|
||||
font-size: 80%;
|
||||
font-family:'Bookman Old Style',Bookman,'URW Bookman L','Palatino Linotype',serif;
|
||||
|
||||
float:left;
|
||||
}
|
||||
|
||||
.menubar .title {
|
||||
float: left;
|
||||
padding-right: 20px;
|
||||
|
||||
font-size: 110%;
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
font-weight: bold;
|
||||
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.menubar .menu_actions {
|
||||
float: left;
|
||||
position:relative;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.menubar .menu_legend {
|
||||
float: right;
|
||||
position:relative;
|
||||
top: 4px;
|
||||
|
||||
}
|
||||
|
||||
h3.sectionHeader {
|
||||
color: black;
|
||||
font-size: 110%;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
padding-left: 8px;
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
background: lightgrey;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: green;
|
||||
}
|
||||
.shortCircuited {
|
||||
color: blue;
|
||||
}
|
||||
.timeout {
|
||||
color: #FF9900; /* shade of orange */
|
||||
}
|
||||
.failure {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.rejected {
|
||||
color: purple;
|
||||
}
|
||||
|
||||
.exceptionsThrown {
|
||||
color: brown;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1100px) {
|
||||
.container {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Hystrix Monitor</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<!-- Setup base for everything -->
|
||||
<link rel="stylesheet" type="text/css" href="../css/global.css" />
|
||||
|
||||
<!-- Our custom CSS -->
|
||||
<link rel="stylesheet" type="text/css" href="monitor.css" />
|
||||
|
||||
<!-- d3 -->
|
||||
<script type="text/javascript" src="/webjars/d3js/3.4.11/d3.min.js" ></script>
|
||||
|
||||
<!-- Javascript to monitor and display -->
|
||||
<script type="text/javascript" src="/webjars/jquery/2.1.1/jquery.min.js" ></script>
|
||||
<script type="text/javascript" src="../js/jquery.tinysort.min.js"></script>
|
||||
<script type="text/javascript" src="../js/tmpl.js"></script>
|
||||
|
||||
<!-- HystrixCommand -->
|
||||
<script type="text/javascript" src="../components/hystrixCommand/hystrixCommand.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../components/hystrixCommand/hystrixCommand.css" />
|
||||
|
||||
<!-- HystrixThreadPool -->
|
||||
<script type="text/javascript" src="../components/hystrixThreadPool/hystrixThreadPool.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="../components/hystrixThreadPool/hystrixThreadPool.css" />
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
<h2><span id="title_name"></span></h2>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="menubar">
|
||||
<div class="title">
|
||||
Circuit
|
||||
</div>
|
||||
<div class="menu_actions">
|
||||
Sort:
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByErrorThenVolume();">Error then Volume</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortAlphabetically();">Alphabetical</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByVolume();">Volume</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByError();">Error</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByLatencyMean();">Mean</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByLatencyMedian();">Median</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByLatency90();">90</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByLatency99();">99</a> |
|
||||
<a href="javascript://" onclick="hystrixMonitor.sortByLatency995();">99.5</a>
|
||||
</div>
|
||||
<div class="menu_legend">
|
||||
<span class="success">Success</span> | <span class="shortCircuited">Short-Circuited</span> | <span class="timeout">Timeout</span> | <span class="rejected">Rejected</span> | <span class="failure">Failure</span> | <span class="errorPercentage">Error %</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dependencies" class="row dependencies"><span class="loading">Loading ...</span></div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="row">
|
||||
<div class="menubar">
|
||||
<div class="title">
|
||||
Thread Pools
|
||||
</div>
|
||||
<div class="menu_actions">
|
||||
Sort: <a href="javascript://" onclick="dependencyThreadPoolMonitor.sortAlphabetically();">Alphabetical</a> |
|
||||
<a href="javascript://" onclick="dependencyThreadPoolMonitor.sortByVolume();">Volume</a> |
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dependencyThreadPools" class="row dependencyThreadPools"><span class="loading">Loading ...</span></div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
/**
|
||||
* Queue up the monitor to start once the page has finished loading.
|
||||
*
|
||||
* This is an inline script and expects to execute once on page load.
|
||||
*/
|
||||
|
||||
// commands
|
||||
var hystrixMonitor = new HystrixCommandMonitor('dependencies', {includeDetailIcon:false});
|
||||
|
||||
var stream = getUrlVars()["stream"];
|
||||
|
||||
console.log("Stream: " + stream)
|
||||
|
||||
if(stream != undefined) {
|
||||
if(getUrlVars()["delay"] != undefined) {
|
||||
stream = stream + "&delay=" + getUrlVars()["delay"];
|
||||
}
|
||||
|
||||
var commandStream = "/proxy.stream?origin=" + stream;
|
||||
var poolStream = "/proxy.stream?origin=" + stream;
|
||||
|
||||
if(getUrlVars()["title"] != undefined) {
|
||||
$('#title_name').html("Hystrix Stream: " + decodeURIComponent(getUrlVars()["title"]))
|
||||
} else {
|
||||
$('#title_name').html("Hystrix Stream: " + decodeURIComponent(stream))
|
||||
}
|
||||
}
|
||||
console.log("Command Stream: " + commandStream)
|
||||
|
||||
$(window).load(function() { // within load with a setTimeout to prevent the infinite spinner
|
||||
setTimeout(function() {
|
||||
if(commandStream == undefined) {
|
||||
console.log("commandStream is undefined")
|
||||
$("#dependencies .loading").html("The 'stream' argument was not provided.");
|
||||
$("#dependencies .loading").addClass("failed");
|
||||
} else {
|
||||
// sort by error+volume by default
|
||||
hystrixMonitor.sortByErrorThenVolume();
|
||||
|
||||
// start the EventSource which will open a streaming connection to the server
|
||||
var source = new EventSource(commandStream);
|
||||
|
||||
// add the listener that will process incoming events
|
||||
source.addEventListener('message', hystrixMonitor.eventSourceMessageListener, false);
|
||||
|
||||
// source.addEventListener('open', function(e) {
|
||||
// console.console.log(">>> opened connection, phase: " + e.eventPhase);
|
||||
// // Connection was opened.
|
||||
// }, false);
|
||||
|
||||
source.addEventListener('error', function(e) {
|
||||
if (e.eventPhase == EventSource.CLOSED) {
|
||||
// Connection was closed.
|
||||
console.log("Connection was closed on error: " + JSON.stringify(e));
|
||||
} else {
|
||||
console.log("Error occurred while streaming: " + JSON.stringify(e));
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
},0);
|
||||
});
|
||||
|
||||
// thread pool
|
||||
var dependencyThreadPoolMonitor = new HystrixThreadPoolMonitor('dependencyThreadPools');
|
||||
|
||||
$(window).load(function() { // within load with a setTimeout to prevent the infinite spinner
|
||||
setTimeout(function() {
|
||||
if(poolStream == undefined) {
|
||||
console.log("poolStream is undefined")
|
||||
$("#dependencyThreadPools .loading").html("The 'stream' argument was not provided.");
|
||||
$("#dependencyThreadPools .loading").addClass("failed");
|
||||
} else {
|
||||
dependencyThreadPoolMonitor.sortByVolume();
|
||||
|
||||
// start the EventSource which will open a streaming connection to the server
|
||||
var source = new EventSource(poolStream);
|
||||
|
||||
// add the listener that will process incoming events
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
},0);
|
||||
});
|
||||
|
||||
//Read a page's GET URL variables and return them as an associative array.
|
||||
// from: http://jquery-howto.blogspot.com/2009/09/get-url-parameters-values-with-jquery.html
|
||||
function getUrlVars()
|
||||
{
|
||||
var vars = [], hash;
|
||||
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
|
||||
for(var i = 0; i < hashes.length; i++)
|
||||
{
|
||||
hash = hashes[i].split('=');
|
||||
vars.push(hash[0]);
|
||||
vars[hash[0]] = hash[1];
|
||||
}
|
||||
return vars;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user