-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4410 from spalger/feature/3270/statusPage
Merge of #3821
- Loading branch information
Showing
9 changed files
with
432 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": "../../../../../.jshintrc.browser" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,60 @@ | ||
<html> | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<title>Kibana Status</title> | ||
<meta name="viewport" content="width=device-width, initial-scale=1"> | ||
|
||
<link rel="shortcut icon" href="/styles/theme/elk.ico"> | ||
<link rel="stylesheet" href="/styles/main.css?_b=@@buildNum"> | ||
<link rel="stylesheet" href="styles/main.css?_b=@@buildNum"> | ||
</head> | ||
<body> | ||
<h1>Kibana Status Page</h1> | ||
<p>Statusy stuff goes here... it's goign to be totally awesome!</p> | ||
<div class="container" ng-controller="StatusPage"> | ||
<header> | ||
<h1> | ||
<strong>Kibana</strong> Status Page | ||
</h1> | ||
</header> | ||
<div> | ||
<section class="section"> | ||
<h4>What is this page?</h4> | ||
<p>This page is your sanity check, and your savior. You can check for potential problems</p> | ||
<p>Here is the status of your kibana instance and the plugins you have installed along with some statistics to asses potential problems.</p> | ||
</section> | ||
<div class="system_status_wrapper system_status_{{ui.systemStatus().label}}"> | ||
<h3 class="title"> | ||
<b>System Status:</b> {{ui.systemStatus().msg}} | ||
</h3> | ||
<table id="plugin_table"> | ||
<tr class="plugin_table_header plugin_row"> | ||
<td class="col-xs-1 plugin_key">Plugin</td> | ||
<td class="col-xs-11 plugin_state">Status</td> | ||
</tr> | ||
<tr ng-repeat="(key, plugin) in ui.plugins" class="plugin_table_plugin plugin_row plugin_status_{{plugin.uiStatus}}"> | ||
<td class="col-xs-1 plugin_key">{{key}}</td> | ||
<td class="col-xs-11 plugin_state">{{plugin.message}}</td> | ||
</tr> | ||
</table> | ||
</div> | ||
<h2>Server Metrics</h2> | ||
<p>Interval of 5 seconds, with a max history of 5 minutes.</p> | ||
<div id="chart_cont" class="row"> | ||
<div ng-repeat="(key, chart) in ui.charts" class="status_chart_wrapper col-md-4"> | ||
<h3 class="title">{{chart.niceName}}</h2> | ||
<h4 class="average"> | ||
<span ng-repeat="average in chart.average track by $index"><span ng-if="$index">, </span>{{average}}</span> | ||
</h4> | ||
<nvd3 options="chart.options" data="chart.data"></nvd3> | ||
</div> | ||
<div class="clearfix"></div> | ||
</div> | ||
</div> | ||
<footer></footer> | ||
</div> | ||
<script type="text/javascript" src="/bower_components/requirejs/require.js?_b=@@buildNum"></script> | ||
<script type="text/javascript" src="/status/require.config.js?_b=@@buildNum"></script> | ||
<script type="text/javascript"> | ||
require(['/status/index.js'], function(kibanaStatus) { kibanaStatus.init(); }); | ||
</script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
define(function (require) { | ||
var angular = require('angular'); | ||
var $ = require('jquery'); | ||
var _ = require('lodash'); | ||
var moment = require('moment'); | ||
var numeral = require('numeral'); | ||
require('nvd3_directives'); | ||
|
||
// Make sure we don't have to deal with statuses by hand | ||
function getStatus(plugin) { | ||
var statusMap = { | ||
green: { | ||
label: 'success', | ||
msg: 'Ready', | ||
idx: 1 | ||
}, | ||
yellow: { | ||
label: 'warning', | ||
msg: 'S.N.A.F.U.', | ||
idx: 2 | ||
}, | ||
red: { | ||
label: 'danger', | ||
msg: 'Danger Will Robinson! Danger!', | ||
idx: 3 | ||
}, | ||
loading: { | ||
label: 'info', | ||
msg: 'Loading...', | ||
idx: 0 | ||
} | ||
}; | ||
if (!_.isObject(plugin) || _.isUndefined(plugin)) { | ||
plugin = {state: plugin}; | ||
} | ||
return statusMap[plugin.state]; | ||
} | ||
|
||
function getLabel(plugin) { return getStatus(plugin).label; } | ||
|
||
// Turns thisIsASentence to | ||
// This Is A Sentence | ||
function niceName(name) { | ||
return name | ||
.split(/(?=[A-Z])/) | ||
.map(function (word) { return word[0].toUpperCase() + _.rest(word).join(''); }) | ||
.join(' '); | ||
} | ||
|
||
function formatNumber(num, which) { | ||
var format = '0.00'; | ||
var postfix = ''; | ||
switch (which) { | ||
case 'time': | ||
return moment(num).format('HH:mm:ss'); | ||
case 'byte': | ||
format += 'b'; | ||
break; | ||
case 'ms': | ||
postfix = 'ms'; | ||
break; | ||
} | ||
return numeral(num).format(format) + postfix; | ||
} | ||
|
||
function numberType(key) { | ||
var byteMetrics = ['heapTotal', 'heapUsed', 'rss']; | ||
var msMetrics = ['delay', 'responseTimeAvg', 'responseTimeMax']; | ||
var preciseMetric = ['requests', 'load']; | ||
if ( byteMetrics.indexOf(key) > -1 ) { | ||
return 'byte'; | ||
} else if (msMetrics.indexOf(key) > -1 ) { | ||
return 'ms'; | ||
} else { | ||
return 'precise'; | ||
} | ||
} | ||
|
||
var makeChartOptions = _.memoize(function (type) { | ||
return { | ||
chart: { | ||
type: 'lineChart', | ||
height: 200, | ||
showLegend: false, | ||
showXAxis: false, | ||
showYAxis: false, | ||
useInteractiveGuideline: true, | ||
tooltips: true, | ||
pointSize: 0, | ||
color: ['#444', '#777', '#aaa'], | ||
margin: { | ||
top: 10, | ||
left: 0, | ||
right: 0, | ||
bottom: 20 | ||
}, | ||
xAxis: { tickFormat: function (d) { return formatNumber(d, 'time'); } }, | ||
yAxis: { tickFormat: function (d) { return formatNumber(d, type); }, }, | ||
y: function (d) { return d.y; }, | ||
x: function (d) { return d.x; } | ||
} | ||
}; | ||
}); | ||
|
||
// The Kibana App | ||
require('modules') | ||
.get('KibanaStatusApp', ['nvd3']) | ||
.controller('StatusPage', function ($scope, $http, $window, $timeout) { | ||
// the object representing all of the elements the ui touches | ||
$scope.ui = { | ||
// show the system status by going through all of the plugins, | ||
// and making sure they're green. | ||
systemStatus: (function () { | ||
// for convenience | ||
function getIdx(plugin) { return getStatus(plugin).idx; } | ||
|
||
return function () { | ||
var currentStatus = 'loading'; | ||
var currentIdx = getIdx(currentStatus); | ||
|
||
// FIXME eh, not too thrilled about this. | ||
var status = _.reduce($scope.ui.plugins, function (curr, plugin, key) { | ||
var pluginIdx = getIdx(plugin); | ||
if (pluginIdx > currentIdx) { | ||
// set the current status | ||
currentStatus = plugin.state; | ||
currentIdx = getIdx(plugin); | ||
} | ||
return currentStatus; | ||
}, 'loading'); | ||
|
||
// give the ui the label for colors and such | ||
return getStatus(status); | ||
}; | ||
}()), | ||
charts: {}, | ||
plugins: [] | ||
}; | ||
|
||
var windowHasFocus = true; | ||
angular.element($window).bind({ | ||
blur: function () { windowHasFocus = false; }, | ||
focus: function () { | ||
windowHasFocus = true; | ||
getAppStatus(); | ||
} | ||
}); | ||
|
||
function getAppStatus() { | ||
// go ahead and get the info you want | ||
$http | ||
.get('/status/health') | ||
.success(function (data) { | ||
// Assign the propper variables to the scope and change them as necessary | ||
|
||
// setup The charts | ||
// wrap the metrics data and append the average | ||
$scope.ui.charts = _.mapValues(data.metrics, function (metric, name) { | ||
|
||
// Metric Values format | ||
// metric: [[xValue, yValue], ...] | ||
// LoadMetric: | ||
// metric: [[xValue, [yValue, yValue2, yValue3]], ...] | ||
// return [ | ||
// {type: 'line', key: name, yAxis: 1, values: [{x: xValue, y: yValue}, ...]}, | ||
// {type: 'line', key: name, yAxis: 1, values: [{x: xValue, y: yValue1}, ...]}, | ||
// {type: 'line', key: name, yAxis: 1, values: [{x: xValue, y: yValue2}, ...]}] | ||
// | ||
// Go through all of the metric values and split the values out. | ||
// returns an array of all of the averages | ||
var metricList = []; | ||
var metricNumberType = numberType(name); | ||
|
||
// convert the [x,y] into {x: x, y: y} | ||
metric.forEach(function (vector) { | ||
vector = _.flatten(vector); | ||
var x = vector.shift(); | ||
vector.forEach(function (yValue, idx) { | ||
if (!metricList[idx]) { | ||
metricList[idx] = { | ||
key: name + idx, | ||
values: [] | ||
}; | ||
} | ||
// unshift to make sure they're in the correct order | ||
metricList[idx].values.unshift({x: x, y: yValue}); | ||
}); | ||
}); | ||
|
||
var average = metricList.map(function (data) { | ||
var uglySum = data.values.reduce(function (sumSoFar, vector) { | ||
return sumSoFar + vector.y; | ||
}, 0); | ||
return formatNumber(uglySum / data.values.length, metricNumberType); | ||
}); | ||
var options = makeChartOptions(metricNumberType); | ||
|
||
return { data: metricList, average: average, niceName: niceName(name), options: options }; | ||
}); | ||
|
||
// give the plugins their proper name so CSS classes can be properply applied | ||
$scope.ui.plugins = _.mapValues(data.status, function (plugin) { | ||
plugin.uiStatus = getLabel(plugin); | ||
return plugin; | ||
}); | ||
|
||
if (windowHasFocus) { | ||
// go ahead and get another status in 5 seconds | ||
$timeout(getAppStatus, 5000); | ||
} | ||
}) | ||
.error(function () { | ||
window.alert('Something went terribly wrong while making the request!!! Perhaps your server is down?'); | ||
}); | ||
} | ||
|
||
// Start it all up | ||
getAppStatus(); | ||
}); | ||
|
||
return { | ||
init: function () { | ||
$(function () { | ||
angular.bootstrap(window.document, ['nvd3', 'KibanaStatusApp']); | ||
}); | ||
} | ||
}; | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
require.config({ | ||
baseUrl: '/', | ||
paths: { | ||
angular: '/bower_components/angular/angular', | ||
css: '/bower_components/require-css/css', | ||
d3: '/bower_components/d3/d3', | ||
jquery: '/bower_components/jquery/dist/jquery', | ||
lodash: '/utils/lodash-mixins/index', | ||
lodash_src: '/bower_components/lodash/lodash', | ||
moment: '/bower_components/moment/moment', | ||
nvd3: '/bower_components/nvd3/build/nv.d3', | ||
nvd3_directives: '/bower_components/angular-nvd3/dist/angular-nvd3', | ||
numeral: '/bower_components/numeral/numeral' | ||
}, | ||
shim: { | ||
angular: { | ||
deps: ['jquery'], | ||
exports: 'angular' | ||
}, | ||
nvd3: ['css!bower_components/nvd3/build/nv.d3.css', 'd3'], | ||
nvd3_directives: ['angular', 'nvd3'] | ||
} | ||
}); |
Oops, something went wrong.