Skip to content

Commit

Permalink
Merge pull request #4410 from spalger/feature/3270/statusPage
Browse files Browse the repository at this point in the history
Merge of #3821
  • Loading branch information
spalger committed Jul 10, 2015
2 parents d6ed73f + f2899f5 commit 21fb120
Show file tree
Hide file tree
Showing 9 changed files with 432 additions and 5 deletions.
1 change: 1 addition & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ module.exports = function (grunt) {
'<%= src %>/kibana/components/*/*.less',
'<%= src %>/kibana/styles/main.less',
'<%= src %>/kibana/components/vislib/styles/main.less',
'<%= src %>/server/plugins/status/public/styles/main.less',
'<%= plugins %>/dashboard/styles/main.less',
'<%= plugins %>/discover/styles/main.less',
'<%= plugins %>/settings/styles/main.less',
Expand Down
5 changes: 4 additions & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"angular-bootstrap": "0.10.0",
"angular-elastic": "2.4.2",
"angular-mocks": "1.2.28",
"angular-nvd3": "https://github.com/krispo/angular-nvd3.git#1.0.0-beta",
"angular-route": "1.2.28",
"angular-ui-ace": "0.2.3",
"bluebird": "~2.9.27",
Expand All @@ -47,10 +48,12 @@
"requirejs-text": "2.0.14",
"marked": "0.3.3",
"numeral": "1.5.3",
"nvd3": "1.7.1",
"leaflet-draw": "0.2.4"
},
"devDependencies": {},
"resolutions": {
"angular": "1.2.28"
"angular": "1.2.28",
"d3": "3.5.5"
}
}
3 changes: 3 additions & 0 deletions src/server/plugins/status/public/.jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../../../../.jshintrc.browser"
}
57 changes: 54 additions & 3 deletions src/server/plugins/status/public/index.html
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>&nbsp;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>
229 changes: 229 additions & 0 deletions src/server/plugins/status/public/index.js
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']);
});
}
};

});
23 changes: 23 additions & 0 deletions src/server/plugins/status/public/require.config.js
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']
}
});
Loading

0 comments on commit 21fb120

Please sign in to comment.