Commit 3c280403 by Dave Syer

Make hystrix dashboard embeddable finally

parent 34cbf7a6
......@@ -20,7 +20,7 @@
<modules>
<module>spring-cloud-netflix-core</module>
<module>spring-cloud-netflix-hystrix</module>
<module>spring-cloud-netflix-hystrix-dashboard</module>
<module>spring-cloud-netflix-turbine</module>
<module>spring-cloud-netflix-zuul</module>
</modules>
......@@ -33,6 +33,11 @@
<version>1.0.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.netflix.archaius</groupId>
<artifactId>archaius-core</artifactId>
<version>${archaius.version}</version>
......
package org.springframework.cloud.netflix.hystrix.annotations;
package org.springframework.cloud.netflix.hystrix;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.cloud.netflix.hystrix.HystrixConfigurationSelector;
import java.lang.annotation.*;
......
......@@ -8,7 +8,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.cloud.netflix.hystrix.annotations.EnableHystrix;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
......
......@@ -3,7 +3,6 @@ package org.springframework.cloud.netflix.hystrix;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.context.annotation.AutoProxyRegistrar;
import org.springframework.cloud.netflix.hystrix.annotations.EnableHystrix;
/**
* Created by sgibb on 6/19/14.
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-netflix-hystrix</artifactId>
<packaging>war</packaging>
<artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
<name>Spring Cloud Netflix Hystrix</name>
<url>http://projects.spring.io/spring-cloud/</url>
<parent>
......@@ -12,32 +11,16 @@
<relativePath>..</relativePath>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<!--skip deploy (this is just a test module) -->
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<spring-cloud-config.version>1.0.0.BUILD-SNAPSHOT</spring-cloud-config.version>
<spring-cloud.version>1.0.0.BUILD-SNAPSHOT</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config</artifactId>
<version>${spring-cloud-config.version}</version>
<artifactId>spring-cloud-starters</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
......@@ -57,10 +40,6 @@
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.eureka</groupId>
<artifactId>eureka-client</artifactId>
</dependency>
......
package org.springframework.cloud.netflix.hystrix.annotations;
package org.springframework.cloud.netflix.hystrix.dashboard;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
......@@ -6,7 +6,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.cloud.netflix.hystrix.dashboard.HystrixDashboardConfiguration;
import org.springframework.context.annotation.Import;
/**
......
package io.spring.cloud.netflix.hystrix;
import io.spring.cloud.netflix.hystrix.stream.MockStreamServlet;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.cloud.netflix.hystrix.annotations.EnableHystrixDashboard;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* Created by sgibb on 7/11/14.
*/
@Configuration
@ComponentScan
@EnableHystrixDashboard
@EnableAutoConfiguration
public class HystrixDashboardApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(HystrixDashboardApplication.class).web(true);
}
public static void main(String[] args) {
new SpringApplicationBuilder(HystrixDashboardApplication.class).web(true).run(args);
}
@Bean
public ServletRegistrationBean mockStreamServlet() {
return new ServletRegistrationBean(new MockStreamServlet(), "/mock.stream");
}
}
package io.spring.cloud.netflix.hystrix.stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.Charset;
/**
* Simulate an event stream URL by retrieving pre-canned data instead of going to live servers.
*/
public class MockStreamServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger logger = LoggerFactory.getLogger(MockStreamServlet.class);
public MockStreamServlet() {
super();
}
/**
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String filename = request.getParameter("file");
if (filename == null) {
// default to using hystrix.stream
filename = "hystrix.stream";
} else {
// strip any .. / characters to avoid security problems
filename = filename.replaceAll("\\.\\.", "");
filename = filename.replaceAll("/", "");
}
int delay = 500;
String delayArg = request.getParameter("delay");
if (delayArg != null) {
delay = Integer.parseInt(delayArg);
}
int batch = 1;
String batchArg = request.getParameter("batch");
if (batchArg != null) {
batch = Integer.parseInt(batchArg);
}
String data = getFileFromPackage(filename);
String lines[] = data.split("\n");
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
int batchCount = 0;
// loop forever unless the user closes the connection
for (;;) {
for (String s : lines) {
s = s.trim();
if (s.length() > 0) {
try {
response.getWriter().println(s);
response.getWriter().println(""); // a newline is needed after each line for the events to trigger
response.getWriter().flush();
batchCount++;
} catch (Exception e) {
logger.warn("Exception writing mock data to output.", e);
// most likely the user closed the connection
return;
}
if (batchCount == batch) {
// we insert the delay whenever we finish a batch
try {
// simulate the delays we get from the real feed
Thread.sleep(delay);
} catch (InterruptedException e) {
// ignore
}
// reset
batchCount = 0;
}
}
}
}
}
private String getFileFromPackage(String filename) {
try {
String file = "/" + this.getClass().getPackage().getName().replace('.', '/') + "/" + filename;
InputStream is = this.getClass().getResourceAsStream(file);
try {
/* this is FAR too much work just to get a string from a file */
BufferedReader in = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
StringWriter s = new StringWriter();
int c = -1;
while ((c = in.read()) > -1) {
s.write(c);
}
return s.toString();
} finally {
is.close();
}
} catch (Exception e) {
throw new RuntimeException("Could not find file: " + filename, e);
}
}
}
#org.springframework.context.ApplicationListener=\
#org.springframework.cloud.netflix.eureka.EurekaStartingListener,\
#org.springframework.cloud.netflix.eureka.EurekaUpListener,\
#org.springframework.cloud.netflix.eureka.EurekaOutOfServiceListener
#org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
#org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration,\
#org.springframework.cloud.netflix.feign.FeignAutoConfiguration
info:
component: Hystrix Dashboard
endpoints:
restart:
enabled: true
shutdown:
enabled: true
server:
port: 7979
logging:
level: INFO
eureka:
client:
#Region where eureka is deployed -For AWS specify one of the AWS regions, for other datacenters specify a arbitrary string
#indicating the region.This is normally specified as a -D option (eg) -Deureka.region=us-east-1
region: default
#For eureka clients running in eureka server, it needs to connect to servers in other zones
preferSameZone: false
#Change this if you want to use a DNS based lookup for determining other eureka servers. For example
#of specifying the DNS entries, check the eureka-client-test.properties, eureka-client-prod.properties
#shouldUseDns: false
us-east-1:
availabilityZones: default
serviceUrl:
default: http://localhost:8080/eureka/v2/
defaultZone: http://localhost:8080/eureka/v2/
instance:
#Virtual host name by which the clients identifies this service
virtualHostName: ${spring.application.name}
spring:
application:
name: hystrixdashboard
platform:
config:
uri: http://localhost:${config.port:8888}
\ No newline at end of file
log4j.rootLogger=INFO, FILE
log4j.appender.FILE=org.apache.log4j.ConsoleAppender
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p %C:%L [%C{1}] [%M]: %m%n
log4j.appender.FILE.httpclient=ERROR
.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;
}
}
<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> -->
<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>
<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>
.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;
}
<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>
\ No newline at end of file
<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>
@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;
}
}
/*
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;
}
\ No newline at end of file
/* 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 */
\ No newline at end of file
/* 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; }
\ No newline at end of file
/* 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 */
\ No newline at end of file
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.
\ No newline at end of file
/* 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 */
\ No newline at end of file
<!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="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
<script>
function sendToMonitor() {
if($('#stream').val().length > 0) {
var url = "./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
&nbsp;&nbsp;&nbsp;&nbsp;
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>
\ No newline at end of file
Copyright (c) 2012, Michael Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name Michael Bostock may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* 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);
\ No newline at end of file
//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);
.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;
}
}
<!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="../js/d3.v2.min.js"></script>
<!-- Javascript to monitor and display -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></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"];
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))
}
}
$(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: " + e);
} else {
console.log("Error occurred while streaming: " + 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>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment