Просмотр исходного кода

feat(alerting): thresholds rethink

Torkel Ödegaard 9 лет назад
Родитель
Сommit
ed7a539ddb

+ 60 - 42
public/app/plugins/panel/graph/alert_tab_ctrl.ts

@@ -22,7 +22,7 @@ var alertQueryDef = new QueryPartDef({
 export class AlertTabCtrl {
   panel: any;
   panelCtrl: any;
-  metricTargets = [{ refId: '- select query -' } ];
+  metricTargets;
   handlers = [{text: 'Grafana', value: 1}, {text: 'External', value: 0}];
   transforms = [
     {
@@ -36,6 +36,7 @@ export class AlertTabCtrl {
   ];
   aggregators = ['avg', 'sum', 'min', 'max', 'last'];
   alert: any;
+  thresholds: any;
   query: any;
   queryParams: any;
   transformDef: any;
@@ -45,24 +46,6 @@ export class AlertTabCtrl {
     {text: '=', value: '='},
   ];
 
-  defaultValues = {
-    frequency: '60s',
-    notify: [],
-    enabled: false,
-    handler: 1,
-    warn: { op: '>', level: undefined },
-    critical: { op: '>', level: undefined },
-    query: {
-      refId: 'A',
-      from: '5m',
-      to: 'now',
-    },
-    transform: {
-      type: 'aggregation',
-      method: 'avg'
-    }
-  };
-
   /** @ngInject */
   constructor($scope, private $timeout) {
     this.panelCtrl = $scope.ctrl;
@@ -70,7 +53,7 @@ export class AlertTabCtrl {
     $scope.ctrl = this;
 
     this.metricTargets = this.panel.targets.map(val => val);
-    this.initAlertModel();
+    this.initModel();
 
     // set panel alert edit mode
     $scope.$on("$destroy", () => {
@@ -79,32 +62,63 @@ export class AlertTabCtrl {
     });
   }
 
-  initAlertModel() {
-    if (!this.panel.alert) {
+  getThresholdWithDefaults(thresholds, type, copyFrom) {
+    var threshold = thresholds[type] || {};
+    var defaultValue = (copyFrom[type] || {}).value || undefined;
+
+    threshold.op = threshold.op || '>';
+    threshold.value = threshold.value || defaultValue;
+    return threshold;
+  }
+
+  initThresholdsOnlyMode() {
+    if (!this.panel.thresholds) {
       return;
     }
 
-    this.alert = this.panel.alert;
+    this.thresholds = this.panel.thresholds;
+
+    // set threshold defaults
+    this.thresholds.warn = this.getThresholdWithDefaults(this.thresholds, 'warn', {});
+    this.thresholds.crit = this.getThresholdWithDefaults(this.thresholds, 'crit', {});
+
+    this.panelCtrl.editingAlert = true;
+    this.panelCtrl.render();
+  }
+
+  initModel() {
+    var alert = this.alert = this.panel.alert = this.panel.alert || {};
+
+    // set threshold defaults
+    alert.thresholds = alert.thresholds || {};
+    alert.thresholds.warn = this.getThresholdWithDefaults(alert.thresholds, 'warn', this.panel.thresholds);
+    alert.thresholds.crit = this.getThresholdWithDefaults(alert.thresholds, 'crit', this.panel.thresholds);
 
-    // set defaults
-    _.defaults(this.alert, this.defaultValues);
+    alert.frequency = alert.frequency || '60s';
+    alert.handler = alert.handler || 1;
+    alert.notifications = alert.notifications || [];
 
-    var defaultName = (this.panelCtrl.dashboard.title + ' ' + this.panel.title + ' alert');
-    this.alert.name = this.alert.name || defaultName;
-    this.alert.description = this.alert.description || defaultName;
+    alert.query = alert.query || {};
+    alert.query.refId = alert.query.refId || 'A';
+    alert.query.from = alert.query.from || '5m';
+    alert.query.to = alert.query.to || 'now';
+
+    alert.transform = alert.transform || {};
+    alert.transform.type = alert.transform.type || 'aggregation';
+    alert.transform.method = alert.transform.method || 'avg';
+
+    var defaultName = this.panel.title + ' alert';
+    alert.name = alert.name || defaultName;
+    alert.description = alert.description || defaultName;
 
     // great temp working model
     this.queryParams = {
-      params: [
-        this.alert.query.refId,
-        this.alert.query.from,
-        this.alert.query.to
-      ]
+      params: [alert.query.refId, alert.query.from, alert.query.to]
     };
 
     // init the query part components model
     this.query = new QueryPart(this.queryParams, alertQueryDef);
-    this.transformDef = _.findWhere(this.transforms, {type: this.alert.transform.type});
+    this.transformDef = _.findWhere(this.transforms, {type: alert.transform.type});
 
     this.panelCtrl.editingAlert = true;
     this.panelCtrl.render();
@@ -135,22 +149,26 @@ export class AlertTabCtrl {
     }
   }
 
-  operatorChanged() {
-    this.panelCtrl.render();
-  }
-
   delete() {
+    delete this.alert;
     delete this.panel.alert;
-    this.panelCtrl.editingAlert = false;
-    this.panelCtrl.render();
+    // clear thresholds
+    if (this.panel.thresholds) {
+      this.panel.thresholds = {};
+    }
+    this.initModel();
   }
 
   enable() {
+    if (this.thresholds) {
+      delete this.thresholds;
+      this.panelCtrl.
+    }
     this.panel.alert = {};
-    this.initAlertModel();
+    this.initModel();
   }
 
-  levelsUpdated() {
+  thresholdsUpdated() {
     this.panelCtrl.render();
   }
 }

+ 67 - 64
public/app/plugins/panel/graph/graph.js

@@ -5,7 +5,7 @@ define([
   'lodash',
   'app/core/utils/kbn',
   './graph_tooltip',
-  './alert_handle',
+  './thresholds',
   'jquery.flot',
   'jquery.flot.selection',
   'jquery.flot.time',
@@ -15,7 +15,7 @@ define([
   'jquery.flot.crosshair',
   './jquery.flot.events',
 ],
-function (angular, $, moment, _, kbn, GraphTooltip, AlertHandle) {
+function (angular, $, moment, _, kbn, GraphTooltip, thresholds) {
   'use strict';
 
   var module = angular.module('grafana.directives');
@@ -23,7 +23,7 @@ function (angular, $, moment, _, kbn, GraphTooltip, AlertHandle) {
   var panelWidthCache = {};
 
   // systemjs export
-  var AlertHandleManager = AlertHandle.AlertHandleManager;
+  var ThresholdControls = thresholds.ThresholdControls;
 
   module.directive('grafanaGraph', function($rootScope, timeSrv) {
     return {
@@ -38,7 +38,7 @@ function (angular, $, moment, _, kbn, GraphTooltip, AlertHandle) {
         var legendSideLastValue = null;
         var rootScope = scope.$root;
         var panelWidth = 0;
-        var alertHandles;
+        var thresholdControls;
 
         rootScope.onAppEvent('setCrosshair', function(event, info) {
           // do not need to to this if event is from this panel
@@ -167,8 +167,8 @@ function (angular, $, moment, _, kbn, GraphTooltip, AlertHandle) {
             rightLabel[0].style.marginTop = (getLabelWidth(panel.yaxes[1].label, rightLabel) / 2) + 'px';
           }
 
-          if (alertHandles) {
-            alertHandles.draw(plot);
+          if (thresholdControls) {
+            thresholdControls.draw(plot);
           }
         }
 
@@ -192,14 +192,14 @@ function (angular, $, moment, _, kbn, GraphTooltip, AlertHandle) {
 
           // give space to alert editing
           if (ctrl.editingAlert) {
-            if (!alertHandles) {
+            if (!thresholdControls) {
               elem.css('margin-right', '220px');
-              alertHandles = new AlertHandleManager(ctrl);
+              thresholdControls = new ThresholdControls(ctrl);
             }
-          } else if (alertHandles) {
+          } else if (thresholdControls) {
             elem.css('margin-right', '0');
-            alertHandles.cleanUp();
-            alertHandles = null;
+            thresholdControls.cleanUp();
+            thresholdControls = null;
           }
 
           var stack = panel.stack ? true : null;
@@ -333,70 +333,73 @@ function (angular, $, moment, _, kbn, GraphTooltip, AlertHandle) {
         }
 
         function addGridThresholds(options, panel) {
+          var thresholds = panel.thresholds;
+
+          // use alert thresholds if there are any
           if (panel.alert) {
-            var crit = panel.alert.critical;
-            var warn = panel.alert.warn;
-            var critEdge = Infinity;
-            var warnEdge = crit.level;
-
-            if (_.isNumber(crit.level)) {
-              if (crit.op === '<') {
-                critEdge = -Infinity;
-              }
+            thresholds = panel.alert.thresholds;
+          }
 
-              // fill
-              options.grid.markings.push({
-                yaxis: {from: crit.level, to: critEdge},
-                color: 'rgba(234, 112, 112, 0.10)',
-              });
-
-              // line
-              options.grid.markings.push({
-                yaxis: {from: crit.level, to: crit.level},
-                color: '#ed2e18'
-              });
-            }
+          var crit = thresholds.crit;
+          var warn = thresholds.warn;
+          var critEdge = Infinity;
+          var warnEdge = crit.value;
 
-            if (_.isNumber(warn.level)) {
-              // if (warn.op === '<') {
-              // }
-
-              // fill
-              options.grid.markings.push({
-                yaxis: {from: warn.level, to: warnEdge},
-                color: 'rgba(216, 200, 27, 0.10)',
-              });
-
-              // line
-              options.grid.markings.push({
-                yaxis: {from: warn.level, to: warn.level},
-                color: '#F79520'
-              });
+          if (_.isNumber(crit.value)) {
+            if (crit.op === '<') {
+              critEdge = -Infinity;
             }
 
-            return;
+            // fill
+            options.grid.markings.push({
+              yaxis: {from: crit.value, to: critEdge},
+              color: 'rgba(234, 112, 112, 0.10)',
+            });
+
+            // line
+            options.grid.markings.push({
+              yaxis: {from: crit.value, to: crit.value},
+              color: '#ed2e18'
+            });
           }
 
-          if (_.isNumber(panel.grid.threshold1)) {
-            var limit1 = panel.grid.thresholdLine ? panel.grid.threshold1 : (panel.grid.threshold2 || null);
+          if (_.isNumber(warn.value)) {
+            // if (warn.op === '<') {
+            // }
+
+            // fill
             options.grid.markings.push({
-              yaxis: { from: panel.grid.threshold1, to: limit1 },
-              color: panel.grid.threshold1Color
+              yaxis: {from: warn.value, to: warnEdge},
+              color: 'rgba(216, 200, 27, 0.10)',
             });
 
-            if (_.isNumber(panel.grid.threshold2)) {
-              var limit2;
-              if (panel.grid.thresholdLine) {
-                limit2 = panel.grid.threshold2;
-              } else {
-                limit2 = panel.grid.threshold1 > panel.grid.threshold2 ?  -Infinity : +Infinity;
-              }
-              options.grid.markings.push({
-                yaxis: { from: panel.grid.threshold2, to: limit2 },
-                color: panel.grid.threshold2Color
-              });
-            }
+            // line
+            options.grid.markings.push({
+              yaxis: {from: warn.value, to: warn.value},
+              color: '#F79520'
+            });
           }
+
+          // if (_.isNumber(panel.grid.threshold1)) {
+          //   var limit1 = panel.grid.thresholdLine ? panel.grid.threshold1 : (panel.grid.threshold2 || null);
+          //   options.grid.markings.push({
+          //     yaxis: { from: panel.grid.threshold1, to: limit1 },
+          //     color: panel.grid.threshold1Color
+          //   });
+          //
+          //   if (_.isNumber(panel.grid.threshold2)) {
+          //     var limit2;
+          //     if (panel.grid.thresholdLine) {
+          //       limit2 = panel.grid.threshold2;
+          //     } else {
+          //       limit2 = panel.grid.threshold1 > panel.grid.threshold2 ?  -Infinity : +Infinity;
+          //     }
+          //     options.grid.markings.push({
+          //       yaxis: { from: panel.grid.threshold2, to: limit2 },
+          //       color: panel.grid.threshold2Color
+          //     });
+          //   }
+          // }
         }
 
         function addAnnotations(options) {

+ 5 - 6
public/app/plugins/panel/graph/module.ts

@@ -54,11 +54,9 @@ class GraphCtrl extends MetricsPanelCtrl {
     xaxis: {
       show: true
     },
-    grid          : {
-      threshold1: null,
-      threshold2: null,
-      threshold1Color: 'rgba(216, 200, 27, 0.27)',
-      threshold2Color: 'rgba(234, 112, 112, 0.22)'
+    thresholds: {
+      warn: {op: '>', level: undefined},
+      crit: {op: '>', level: undefined},
     },
     // show/hide lines
     lines         : true,
@@ -115,7 +113,7 @@ class GraphCtrl extends MetricsPanelCtrl {
 
     _.defaults(this.panel, this.panelDefaults);
     _.defaults(this.panel.tooltip, this.panelDefaults.tooltip);
-    _.defaults(this.panel.grid, this.panelDefaults.grid);
+    _.defaults(this.panel.thresholds, this.panelDefaults.thresholds);
     _.defaults(this.panel.legend, this.panelDefaults.legend);
 
     this.colors = $scope.$root.colors;
@@ -132,6 +130,7 @@ class GraphCtrl extends MetricsPanelCtrl {
     this.addEditorTab('Axes', 'public/app/plugins/panel/graph/tab_axes.html', 2);
     this.addEditorTab('Legend', 'public/app/plugins/panel/graph/tab_legend.html', 3);
     this.addEditorTab('Display', 'public/app/plugins/panel/graph/tab_display.html', 4);
+
     if (config.alertingEnabled) {
       this.addEditorTab('Alerting', graphAlertEditor, 5);
     }

+ 35 - 11
public/app/plugins/panel/graph/partials/tab_alerting.html

@@ -1,5 +1,29 @@
 
-<div ng-if="ctrl.panel.alert">
+<div ng-if="ctrl.thresholds">
+  <div class="gf-form-group">
+    <h5 class="section-heading">Visual Thresholds</h5>
+    <div class="gf-form-inline">
+      <div class="gf-form">
+        <span class="gf-form-label">
+          <i class="icon-gf icon-gf-warn alert-icon-critical"></i>
+          Critcal if
+        </span>
+        <metric-segment-model property="ctrl.thresholds.crit.op" options="ctrl.operatorList" custom="false" css-class="query-segment-operator" on-change="ctrl.thresholdsUpdated()"></metric-segment-model>
+        <input class="gf-form-input max-width-7" type="number" ng-model="ctrl.thresholds.crit.value" ng-change="ctrl.thresholdsUpdated()"></input>
+      </div>
+      <div class="gf-form">
+        <span class="gf-form-label">
+          <i class="icon-gf icon-gf-warn alert-icon-warn"></i>
+          Warn if
+        </span>
+        <metric-segment-model property="ctrl.thresholds.warn.op" options="ctrl.operatorList" custom="false" css-class="query-segment-operator" on-change="ctrl.thresholdsUpdated()"></metric-segment-model>
+        <input class="gf-form-input max-width-7" type="number" ng-model="ctrl.thresholds.warn.value" ng-change="ctrl.thresholdsUpdated()"></input>
+      </div>
+    </div>
+  </div>
+</div>
+
+<div ng-if="ctrl.alert">
   <div class="editor-row">
     <div class="gf-form-group section" >
       <h5 class="section-heading">Alert Query</h5>
@@ -39,23 +63,23 @@
     </div>
 
     <div class="gf-form-group section">
-      <h5 class="section-heading">Levels</h5>
+      <h5 class="section-heading">Thresholds</h5>
       <div class="gf-form-inline">
-				<div class="gf-form">
+        <div class="gf-form">
           <span class="gf-form-label">
             <i class="icon-gf icon-gf-warn alert-icon-critical"></i>
             Critcal if
           </span>
-          <metric-segment-model property="ctrl.alert.critical.op" options="ctrl.levelOpList" custom="false" css-class="query-segment-operator" on-change="ctrl.operatorChanged()"></metric-segment-model>
-          <input class="gf-form-input max-width-7" type="number" ng-model="ctrl.alert.critical.level" ng-change="ctrl.levelsUpdated()"></input>
+          <metric-segment-model property="ctrl.alert.thresholds.crit.op" options="ctrl.operatorList" custom="false" css-class="query-segment-operator" on-change="ctrl.thresholdsUpdated()"></metric-segment-model>
+          <input class="gf-form-input max-width-7" type="number" ng-model="ctrl.alert.thresholds.crit.value" ng-change="ctrl.thresholdsUpdated()"></input>
         </div>
-				<div class="gf-form">
+        <div class="gf-form">
           <span class="gf-form-label">
             <i class="icon-gf icon-gf-warn alert-icon-warn"></i>
             Warn if
           </span>
-          <metric-segment-model property="ctrl.alert.warn.op" options="ctrl.levelOpList" custom="false" css-class="query-segment-operator" on-change="ctrl.operatorChanged()"></metric-segment-model>
-          <input class="gf-form-input max-width-7" type="number" ng-model="ctrl.alert.warn.level" ng-change="ctrl.levelsUpdated()"></input>
+          <metric-segment-model property="ctrl.alert.thresholds.warn.op" options="ctrl.operatorList" custom="false" css-class="query-segment-operator" on-change="ctrl.thresholdsUpdated()"></metric-segment-model>
+          <input class="gf-form-input max-width-7" type="number" ng-model="ctrl.alert.thresholds.warn.value" ng-change="ctrl.thresholdsUpdated()"></input>
         </div>
       </div>
     </div>
@@ -111,10 +135,10 @@
 
 <div class="editor-row">
   <div class="gf-form-button-row">
-    <button class="btn btn-danger" ng-click="ctrl.delete()" ng-show="ctrl.panel.alert">Delete</button>
-    <button class="btn btn-inverse" ng-click="ctrl.enable()" ng-hide="ctrl.panel.alert">
+    <button class="btn btn-danger" ng-click="ctrl.delete()" ng-show="ctrl.alert">Delete</button>
+    <button class="btn btn-inverse" ng-click="ctrl.enable()" ng-hide="ctrl.alert">
       <i class="icon-gf icon-gf-alert"></i>
-      Add Alert
+      Create Alert
     </button>
   </div>
 </div>

+ 0 - 29
public/app/plugins/panel/graph/tab_axes.html

@@ -42,33 +42,4 @@
 		<gf-form-switch class="gf-form" label="Show" label-class="width-5" checked="ctrl.panel.xaxis.show" on-change="ctrl.render()"></gf-form-switch>
 	</div>
 
-  <div class="section gf-form-group">
-    <h5 class="section-heading">Thresholds</h5>
-    <div class="gf-form-inline">
-      <div class="gf-form">
-        <label class="gf-form-label width-6">Level 1</label>
-        <input type="number" class="gf-form-input max-width-5" ng-model="ctrl.panel.grid.threshold1" ng-change="ctrl.render()" ng-model-onblur>
-      </div>
-      <div class="gf-form">
-        <label class="gf-form-label width-5">Color</label>
-        <div class="gf-form-label">
-          <spectrum-picker ng-model="ctrl.panel.grid.threshold1Color" ng-change="ctrl.render()" ></spectrum-picker>
-        </div>
-      </div>
-    </div>
-    <div class="gf-form-inline">
-      <div class="gf-form">
-        <label class="gf-form-label width-6">Level 2</label>
-        <input type="number" class="gf-form-input max-width-5" ng-model="ctrl.panel.grid.threshold2" ng-change="ctrl.render()" ng-model-onblur>
-      </div>
-      <div class="gf-form">
-        <label class="gf-form-label width-5">Color</label>
-        <div class="gf-form-label">
-          <spectrum-picker ng-model="ctrl.panel.grid.threshold2Color" ng-change="ctrl.render()" ></spectrum-picker>
-        </div>
-      </div>
-    </div>
-
-    <gf-form-switch class="gf-form" label="Line Mode" label-class="width-6" checked="ctrl.panel.grid.thresholdLine" on-change="ctrl.render()"></gf-form-switch>
-  </div>
 </div>

+ 20 - 19
public/app/plugins/panel/graph/alert_handle.ts → public/app/plugins/panel/graph/thresholds.ts

@@ -4,14 +4,14 @@ import 'jquery.flot';
 import $ from 'jquery';
 import _ from 'lodash';
 
-export class AlertHandleManager {
+export class ThresholdControls {
   plot: any;
   placeholder: any;
   height: any;
-  alert: any;
+  thresholds: any;
 
   constructor(private panelCtrl) {
-    this.alert = panelCtrl.panel.alert;
+    this.thresholds = this.panelCtrl.thresholds;
   }
 
   getHandleInnerHtml(type, op, value) {
@@ -36,7 +36,7 @@ export class AlertHandleManager {
     `;
   }
 
-  setupDragging(handleElem, levelModel) {
+  setupDragging(handleElem, threshold) {
     var isMoving = false;
     var lastY = null;
     var posTop;
@@ -57,11 +57,11 @@ export class AlertHandleManager {
     function stopped() {
       isMoving = false;
       // calculate graph level
-      var graphLevel = plot.c2p({left: 0, top: posTop}).y;
-      graphLevel = parseInt(graphLevel.toFixed(0));
-      levelModel.level = graphLevel;
+      var graphValue = plot.c2p({left: 0, top: posTop}).y;
+      graphValue = parseInt(graphValue.toFixed(0));
+      threshold.value = graphValue;
 
-      var levelCanvasPos = plot.p2c({x: 0, y: graphLevel});
+      var valueCanvasPos = plot.p2c({x: 0, y: graphValue});
 
       handleElem.off("mousemove", dragging);
       handleElem.off("mouseup", dragging);
@@ -90,28 +90,28 @@ export class AlertHandleManager {
 
   renderHandle(type, model, defaultHandleTopPos) {
     var handleElem = this.placeholder.find(`.alert-handle-wrapper--${type}`);
-    var level = model.level;
-    var levelStr = level;
+    var value = model.value;
+    var valueStr = value;
     var handleTopPos = 0;
 
     // handle no value
-    if (!_.isNumber(level)) {
-      levelStr = '';
+    if (!_.isNumber(value)) {
+      valueStr = '';
       handleTopPos = defaultHandleTopPos;
     } else {
-      var levelCanvasPos = this.plot.p2c({x: 0, y: level});
-      handleTopPos = Math.min(Math.max(levelCanvasPos.top, 0), this.height) - 6;
+      var valueCanvasPos = this.plot.p2c({x: 0, y: value});
+      handleTopPos = Math.min(Math.max(valueCanvasPos.top, 0), this.height) - 6;
     }
 
     if (handleElem.length === 0) {
-      handleElem = $(this.getFullHandleHtml(type, model.op, levelStr));
+      handleElem = $(this.getFullHandleHtml(type, model.op, valueStr));
       this.placeholder.append(handleElem);
       this.setupDragging(handleElem, model);
     } else {
-      handleElem.html(this.getHandleInnerHtml(type, model.op, levelStr));
+      handleElem.html(this.getHandleInnerHtml(type, model.op, valueStr));
     }
 
-    handleElem.toggleClass('alert-handle-wrapper--no-value', levelStr === '');
+    handleElem.toggleClass('alert-handle-wrapper--no-value', valueStr === '');
     handleElem.css({top: handleTopPos});
   }
 
@@ -120,9 +120,10 @@ export class AlertHandleManager {
     this.placeholder = plot.getPlaceholder();
     this.height = plot.height();
 
-    this.renderHandle('critical', this.alert.critical, 10);
-    this.renderHandle('warn', this.alert.warn, this.height-30);
+    this.renderHandle('crit', this.thresholds.crit, 10);
+    this.renderHandle('warn', this.thresholds.warn, this.height-30);
   }
+    debugger;
 
 }
 

+ 4 - 1
public/sass/base/_grafana_icons.scss

@@ -31,7 +31,7 @@
 .icon-gf-raintank_wordmark:before {
     content: "\e600";
 }
-.icon-gf-raintank_icn:before {
+.micon-gf-raintank_icn:before {
     content: "\e601";
 }
 .icon-gf-raintank_r-icn:before {
@@ -88,6 +88,9 @@
 .icon-gf-critical:before {
     content: "\e610";
 }
+.icon-gf-crit:before {
+  content: "\e610";
+}
 .icon-gf-online:before {
     content: "\e611";
 }

+ 1 - 0
public/sass/components/_alerts.scss

@@ -15,6 +15,7 @@
   color: $warn;
 }
 
+.alert-icon-crit,
 .alert-icon-critical {
   color: $critical;
 }

+ 1 - 1
public/sass/components/_panel_graph.scss

@@ -363,7 +363,7 @@
     }
   }
 
-  &--critical {
+  &--crit{
     right: -105px;
     width: 123px;