mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
Moved metrics to panel settings and added an initial implementation for an editor
This commit is contained in:
parent
5577058be1
commit
b82b159fc0
3 changed files with 187 additions and 59 deletions
|
@ -1,2 +1,66 @@
|
|||
<h5>General</h5>
|
||||
<div class="row-fluid">
|
||||
<div class="span6">
|
||||
<label class="small">Persistent id field
|
||||
<i class="icon-question-sign" bs-tooltip="'choose a field that doesn't change on node restart'"></i></label>
|
||||
<input type="text" bs-typeahead="fields.list" class="input-large" ng-model="panel.node_persistent_field"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span6">
|
||||
<label class="small">Display field
|
||||
<i class="icon-question-sign" bs-tooltip="'will be used as the name of each row'"></i></label>
|
||||
<input type="text" bs-typeahead="fields.list" class="input-large" ng-model="panel.node_display_field"/>
|
||||
</div>
|
||||
</div>
|
||||
<h5>Metrics</h5>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<table class="table table-condensed table-striped">
|
||||
<thead>
|
||||
<th>Name</th>
|
||||
<th>Field</th>
|
||||
<th>Warning</th>
|
||||
<th>Error</th>
|
||||
<th>Prec.</th>
|
||||
<th/>
|
||||
<th/>
|
||||
</thead>
|
||||
<tr ng-repeat="metric in panel.metrics">
|
||||
<td ng-show="editedMetricIndex != $index">{{metric.name}}</td>
|
||||
<td ng-show="editedMetricIndex != $index">{{metric.field}}</td>
|
||||
<td ng-show="editedMetricIndex != $index">{{formatAlert(metric.warning)}}</td>
|
||||
<td ng-show="editedMetricIndex != $index">{{formatAlert(metric.error)}}</td>
|
||||
<td ng-show="editedMetricIndex != $index">{{metric.decimals}}</td>
|
||||
<td ng-show="editedMetricIndex != $index">
|
||||
<i ng-show="editedMetricIndex < 0" ng-click="editedMetricIndex=$index" class="pointer icon-edit"></i></td>
|
||||
<td ng-show="editedMetricIndex != $index">
|
||||
<i ng-show="editedMetricIndex < 0 && !$first" ng-click="_.move(panel.metrics,$index,$index-1)" class="pointer icon-arrow-up"></i>
|
||||
</td>
|
||||
<td ng-show="editedMetricIndex != $index">
|
||||
<i ng-show="editedMetricIndex < 0 && !$last" ng-click="_.move(panel.metrics,$index,$index+1)" class="pointer icon-arrow-down"></i>
|
||||
</td>
|
||||
<td ng-show="editedMetricIndex == $index">
|
||||
<input type="text" class="input-medium" ng-model="metric.name" ng-required/></td>
|
||||
<td ng-show="editedMetricIndex == $index">
|
||||
<input type="text" ng-model="metric.field" bs-typeahead="fields.list" ng-required/>
|
||||
</td>
|
||||
<td ng-show="editedMetricIndex == $index">
|
||||
<input type="text" style="width: 30px;" alert-value ng-model="metric.warning"/>
|
||||
</td>
|
||||
<td ng-show="editedMetricIndex == $index">
|
||||
<input type="text" style="width: 30px;" alert-value ng-model="metric.error"></td>
|
||||
<td ng-show="editedMetricIndex == $index">
|
||||
<input type="text" style="width: 30px;" ng-model="metric.decimals" value="2" ng-required/></td>
|
||||
<td ng-show="editedMetricIndex == $index">
|
||||
<i class="pointer icon-check" ng-click="editedMetricIndex=-1"></i>
|
||||
</td>
|
||||
<td ng-show="editedMetricIndex == $index">
|
||||
<i ng-click="deleteMetric($index)" class="pointer icon-remove"></i>
|
||||
<td ng-show="editedMetricIndex == $index">
|
||||
<i class="icon"></i></td>
|
||||
</tr>
|
||||
</table>
|
||||
<button ng-hide="editedMetric.index >= 0" type='button' ng-click="addMetric()" class="btn btn-success">Add Metric</button>
|
||||
</div>
|
||||
</div>
|
|
@ -45,7 +45,7 @@
|
|||
<table class="table table-bordered" ng-if="!panel.compact">
|
||||
<thead>
|
||||
<th>node <a id="detail_view_link" ng-href="{{detailViewLink()}}" target="_top" class="btn btn-mini btn-info" ng-disabled="!hasSelected(nodes)" bs-tooltip="detailViewTip()" data-placement="right">nodes dashboard</a></th>
|
||||
<th ng-repeat="metric in metrics" ng-class="alertClass(warnLevels['_global_'][metric.name])">{{metric.name}}</th>
|
||||
<th ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels['_global_'][metric.name])">{{metric.name}}</th>
|
||||
</thead>
|
||||
<tr ng-repeat="node in nodes">
|
||||
<td>
|
||||
|
@ -57,7 +57,7 @@
|
|||
</label>
|
||||
</div>
|
||||
</td>
|
||||
<td ng-repeat="metric in metrics" ng-class="alertClass(warnLevels[node.id][metric.name])">
|
||||
<td ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels[node.id][metric.name])">
|
||||
<div class="marvel-mean pointer" ng-click="metricClick(node,metric)">
|
||||
{{data[node.id+"_"+metric.name].mean / metric.scale | number:metric.decimals}}<br>
|
||||
|
||||
|
@ -66,7 +66,6 @@
|
|||
<div class="marvel-extended pointer" ng-click="metricClick(node,metric)">
|
||||
<span>min: {{data[node.id+"_"+metric.name].min / metric.scale | number:metric.decimals}}</span><br>
|
||||
<span>max: {{data[node.id+"_"+metric.name].max / metric.scale | number:metric.decimals}}</span>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -75,7 +74,7 @@
|
|||
<table class="table table-bordered table-condensed marvel-table" ng-if="panel.compact">
|
||||
<thead>
|
||||
<th>node <a ng-href="{{detailViewLink()}}" class="btn btn-mini btn-info" ng-disabled="!hasSelected(nodes)" bs-tooltip="detailViewTip()" data-placement="right">nodes dashboard</a></th>
|
||||
<th ng-repeat="metric in metrics" ng-class="alertClass(warnLevels['_global_'][metric.name])">{{metric.name}}</th>
|
||||
<th ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels['_global_'][metric.name])">{{metric.name}}</th>
|
||||
</thead>
|
||||
<tr ng-repeat="node in nodes">
|
||||
<td>
|
||||
|
@ -86,7 +85,7 @@
|
|||
{{ node.display_name }}
|
||||
</div>
|
||||
</td>
|
||||
<td ng-repeat="metric in metrics" ng-class="alertClass(warnLevels[node.id][metric.name])">
|
||||
<td ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels[node.id][metric.name])">
|
||||
<div class="pointer" ng-click="metricClick(node,metric)">{{data[node.id+"_"+metric.name].mean / metric.scale | number:metric.decimals}}
|
||||
<div class="marvel-nodes-health-chart pointer" ng-click="metricClick(node,metric)" series="data[node.id+'_'+metric.name+'_history']"></div>
|
||||
</div>
|
||||
|
|
|
@ -25,11 +25,63 @@ define([
|
|||
var _d = {
|
||||
compact: false,
|
||||
node_display_field: "node.name", // used as primary display string for a node.
|
||||
node_persistent_field: "node.transport_address" // used as node identity - i.e., search queries, facets etc.
|
||||
|
||||
node_persistent_field: "node.transport_address", // used as node identity - i.e., search queries, facets etc.
|
||||
metrics: [
|
||||
{
|
||||
name: 'CPU (%)',
|
||||
field: 'process.cpu.percent',
|
||||
warning: 60,
|
||||
error: 90,
|
||||
decimals: 2
|
||||
},
|
||||
{
|
||||
name: 'Load (1m)',
|
||||
field: 'os.load_average.1m',
|
||||
warning: 8,
|
||||
error: 10,
|
||||
decimals: 2
|
||||
},
|
||||
{
|
||||
name: 'System Mem (%)',
|
||||
field: 'os.mem.used_percent',
|
||||
warning: 90,
|
||||
error: 97,
|
||||
decimals: 2
|
||||
},
|
||||
{
|
||||
name: 'Jvm Mem (%)',
|
||||
field: 'os.mem.used_percent',
|
||||
warning: 95,
|
||||
error: 98,
|
||||
decimals: 2
|
||||
},
|
||||
{
|
||||
name: 'Free disk space (GB)',
|
||||
field: 'fs.data.available_in_bytes',
|
||||
scale: 1024 * 1024 * 1024,
|
||||
warning: { threshold: 5, type: "lower_bound" },
|
||||
error: { threshold: 2, type: "lower_bound" },
|
||||
decimals: 2
|
||||
}
|
||||
]
|
||||
};
|
||||
_.defaults($scope.panel, _d);
|
||||
|
||||
|
||||
$scope.editedMetricIndex = -1;
|
||||
|
||||
var _metric_defaults = {name: "", decimals: 2, scale: 1};
|
||||
|
||||
_.each($scope.panel.metrics, function (m) {
|
||||
_.defaults(m, _metric_defaults);
|
||||
if (_.isNumber(m.error)) {
|
||||
m.error = { threshold: m.error, type: "upper_bound"};
|
||||
}
|
||||
if (_.isNumber(m.warning)) {
|
||||
m.warning = { threshold: m.warning, type: "upper_bound"};
|
||||
}
|
||||
});
|
||||
|
||||
$scope.init = function () {
|
||||
$scope.warnLevels = {};
|
||||
$scope.nodes = [];
|
||||
|
@ -127,55 +179,6 @@ define([
|
|||
request,
|
||||
results;
|
||||
|
||||
$scope.metrics = [
|
||||
{
|
||||
name: 'CPU (%)',
|
||||
field: 'process.cpu.percent',
|
||||
warning: 60,
|
||||
error: 90,
|
||||
decimals: 2
|
||||
},
|
||||
{
|
||||
name: 'Load (1m)',
|
||||
field: 'os.load_average.1m',
|
||||
warning: 8,
|
||||
error: 10,
|
||||
decimals: 2
|
||||
},
|
||||
{
|
||||
name: 'System Mem (%)',
|
||||
field: 'os.mem.used_percent',
|
||||
warning: 90,
|
||||
error: 97,
|
||||
decimals: 2
|
||||
},
|
||||
{
|
||||
name: 'Jvm Mem (%)',
|
||||
field: 'os.mem.used_percent',
|
||||
warning: 95,
|
||||
error: 98,
|
||||
decimals: 2
|
||||
},
|
||||
{
|
||||
name: 'Free disk space (GB)',
|
||||
field: 'fs.data.available_in_bytes',
|
||||
scale: 1024 * 1024 * 1024,
|
||||
warning: { threshold: 5, type: "lower_bound" },
|
||||
error: { threshold: 2, type: "lower_bound" },
|
||||
decimals: 2
|
||||
}
|
||||
];
|
||||
|
||||
_.each($scope.metrics, function (m) {
|
||||
_.defaults(m, {scale: 1});
|
||||
if (_.isNumber(m.error)) {
|
||||
m.error = { threshold: m.error, type: "upper_bound"};
|
||||
}
|
||||
if (_.isNumber(m.warning)) {
|
||||
m.warning = { threshold: m.warning, type: "upper_bound"};
|
||||
}
|
||||
});
|
||||
|
||||
request = $scope.ejs.Request().indices(dashboard.indices);
|
||||
|
||||
var time = filterSrv.timeRange('last').to;
|
||||
|
@ -186,7 +189,7 @@ define([
|
|||
.must($scope.ejs.RangeFilter('@timestamp').from(time + '||-10m/m'))
|
||||
.must($scope.ejs.TermsFilter($scope.panel.node_persistent_field, id));
|
||||
|
||||
_.each($scope.metrics, function (m) {
|
||||
_.each($scope.panel.metrics, function (m) {
|
||||
request = request
|
||||
.facet($scope.ejs.StatisticalFacet(id + "_" + m.name)
|
||||
.field(m.field || m.name)
|
||||
|
@ -225,6 +228,13 @@ define([
|
|||
window.location = current;
|
||||
};
|
||||
|
||||
$scope.formatAlert = function (a) {
|
||||
if (!a) {
|
||||
return "";
|
||||
}
|
||||
return (a.type === "upper_bound" ? ">" : "<") + a.threshold;
|
||||
};
|
||||
|
||||
$scope.detailViewLink = function (nodes, fields) {
|
||||
if (nodes === undefined) {
|
||||
nodes = _.where($scope.nodes, {selected: true});
|
||||
|
@ -254,10 +264,10 @@ define([
|
|||
|
||||
$scope.calculateWarnings = function () {
|
||||
$scope.warnLevels = {_global_: {}};
|
||||
_.each($scope.metrics, function (metric) {
|
||||
_.each($scope.panel.metrics, function (metric) {
|
||||
$scope.warnLevels._global_[metric.name] = 0;
|
||||
_.each(_.pluck($scope.nodes, 'id'), function (nodeID) {
|
||||
var level = $scope.alertLevel(metric, $scope.data[nodeID + '_' + metric.name].mean);
|
||||
var level = $scope.alertLevel(metric, ($scope.data[nodeID + '_' + metric.name] || {}).mean);
|
||||
$scope.warnLevels[nodeID] = $scope.warnLevels[nodeID] || {};
|
||||
$scope.warnLevels[nodeID][metric.name] = level;
|
||||
if (level > $scope.warnLevels._global_[metric.name]) {
|
||||
|
@ -306,6 +316,61 @@ define([
|
|||
return [];
|
||||
};
|
||||
|
||||
|
||||
$scope.parseAlert = function (s) {
|
||||
if (!s) {
|
||||
return null;
|
||||
}
|
||||
var ret = { type: "upper_bound"};
|
||||
if (s[0] === '<') {
|
||||
ret.type = "lower_bound";
|
||||
s = s.substr(1);
|
||||
} else if (s[0] === '>') {
|
||||
s = s.substr(1);
|
||||
}
|
||||
|
||||
ret.threshold = parseFloat(s);
|
||||
if (isNaN(ret.threshold)) {
|
||||
return null;
|
||||
}
|
||||
return ret;
|
||||
|
||||
};
|
||||
|
||||
|
||||
$scope.addMetric = function () {
|
||||
$scope.panel.metrics.push(_.defaults({}, _metric_defaults));
|
||||
$scope.editedMetricIndex = $scope.panel.metrics.length - 1;
|
||||
};
|
||||
|
||||
$scope.deleteMetric = function (index) {
|
||||
$scope.panel.metrics = _.without($scope.panel.metrics, $scope.panel.metrics[index]);
|
||||
$scope.editedMetricIndex = -1;
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
||||
module.directive('alertValue', function () {
|
||||
return {
|
||||
require: 'ngModel',
|
||||
link: function (scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function (viewValue) {
|
||||
if (/(<>)?\d+(.\d+)?/.test(viewValue)) {
|
||||
// it is valid
|
||||
ctrl.$setValidity('alertValue', true);
|
||||
return scope.parseAlert(viewValue);
|
||||
} else {
|
||||
// it is invalid, return undefined (no model update)
|
||||
ctrl.$setValidity('alertValue', false);
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
ctrl.$formatters.unshift(function (modelValue) {
|
||||
return scope.formatAlert(modelValue);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
module.directive('marvelNodesHealthChart', function () {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue