Browse Source

Remove built in alerting #100 (#103)

master
Alexey Velikiy 6 years ago committed by GitHub
parent
commit
3609561861
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/colors.ts
  2. 16
      src/controllers/analytic_controller.ts
  3. 10
      src/graph_renderer.ts
  4. 5
      src/models/analytic_unit.ts
  5. 19
      src/module.ts
  6. 18
      src/partials/tab_analytics.html
  7. 18
      src/services/analytic_service.ts
  8. 237
      src/threshold_manager.ts
  9. 142
      src/thresholds_form.ts

1
src/colors.ts

@ -4,7 +4,6 @@ export const PALETTE_ROWS = 4;
export const PALETTE_COLUMNS = 14; export const PALETTE_COLUMNS = 14;
export const DEFAULT_ANNOTATION_COLOR = 'rgba(0, 211, 255, 1)'; export const DEFAULT_ANNOTATION_COLOR = 'rgba(0, 211, 255, 1)';
export const OK_COLOR = 'rgba(11, 237, 50, 1)'; export const OK_COLOR = 'rgba(11, 237, 50, 1)';
export const ALERTING_COLOR = 'rgba(237, 46, 24, 1)';
export const NO_DATA_COLOR = 'rgba(150, 150, 150, 1)'; export const NO_DATA_COLOR = 'rgba(150, 150, 150, 1)';
export const REGION_FILL_ALPHA = 0.09; export const REGION_FILL_ALPHA = 0.09;

16
src/controllers/analytic_controller.ts

@ -367,22 +367,6 @@ export class AnalyticController {
this._statusRunners.delete(analyticUnit.id); this._statusRunners.delete(analyticUnit.id);
} }
// async runEnabledWaiter(analyticUnit: AnalyticUnit) {
// var enabled = await this._analyticService.getAlertEnabled(analyticUnit.id);
// if(analyticUnit.alertEnabled !== enabled) {
// analyticUnit.alertEnabled = enabled;
// this._emitter.emit('anomaly-type-alert-change', analyticUnit);
// }
// }
async toggleAlertEnabled(analyticUnit: AnalyticUnit) {
var enabled = analyticUnit.alertEnabled;
analyticUnit.alertEnabled = undefined;
await this._analyticService.setAlertEnabled(analyticUnit.id, enabled);
analyticUnit.alertEnabled = enabled;
this._emitter.emit('anomaly-type-alert-change', analyticUnit);
}
public getNewTempSegmentId(): SegmentId { public getNewTempSegmentId(): SegmentId {
this._tempIdCounted--; this._tempIdCounted--;
return this._tempIdCounted.toString(); return this._tempIdCounted.toString();

10
src/graph_renderer.ts

@ -1,6 +1,6 @@
import { Segment } from './models/segment'; import { Segment } from './models/segment';
import { GraphTooltip } from './graph_tooltip'; import { GraphTooltip } from './graph_tooltip';
import { ThresholdManager } from './threshold_manager';
import { convertValuesToHistogram, getSeriesValues } from './histogram'; import { convertValuesToHistogram, getSeriesValues } from './histogram';
import { import {
AnalyticController, AnalyticController,
@ -41,7 +41,6 @@ export class GraphRenderer {
private _analyticController: AnalyticController; private _analyticController: AnalyticController;
private data: any; private data: any;
private tooltip: GraphTooltip; private tooltip: GraphTooltip;
private thresholdManager: ThresholdManager;
private panelWidth: number; private panelWidth: number;
private plot: any; private plot: any;
private sortedSeries: any; private sortedSeries: any;
@ -82,7 +81,7 @@ export class GraphRenderer {
// this.eventManager = new EventManager(this.ctrl); // this.eventManager = new EventManager(this.ctrl);
this.flotOptions = {} this.flotOptions = {}
this.thresholdManager = new ThresholdManager(this.ctrl);
this.tooltip = new GraphTooltip( this.tooltip = new GraphTooltip(
$elem, this.dashboard, scope, () => this.sortedSeries, $elem, this.dashboard, scope, () => this.sortedSeries,
this._analyticController.getSegmentsSearcher() this._analyticController.getSegmentsSearcher()
@ -90,7 +89,6 @@ export class GraphRenderer {
// panel events // panel events
this.ctrl.events.on('panel-teardown', () => { this.ctrl.events.on('panel-teardown', () => {
this.thresholdManager = null;
if (this.plot) { if (this.plot) {
this.plot.destroy(); this.plot.destroy();
@ -265,7 +263,6 @@ export class GraphRenderer {
$(`<div class="datapoints-warning flot-temp-elem">${this.ctrl.dataWarning.title}</div>`).appendTo(this.$elem); $(`<div class="datapoints-warning flot-temp-elem">${this.ctrl.dataWarning.title}</div>`).appendTo(this.$elem);
} }
this.thresholdManager.draw(plot);
} }
private _processOffsetHook(plot, gridMargin) { private _processOffsetHook(plot, gridMargin) {
@ -324,8 +321,6 @@ export class GraphRenderer {
return; return;
} }
// give space to alert editing
this.thresholdManager.prepare(this.$elem, this.data);
// un-check dashes if lines are unchecked // un-check dashes if lines are unchecked
this.panel.dashes = this.panel.lines ? this.panel.dashes : false; this.panel.dashes = this.panel.lines ? this.panel.dashes : false;
@ -334,7 +329,6 @@ export class GraphRenderer {
this._buildFlotOptions(this.panel); this._buildFlotOptions(this.panel);
this._prepareXAxis(this.panel); this._prepareXAxis(this.panel);
this._configureYAxisOptions(this.data); this._configureYAxisOptions(this.data);
this.thresholdManager.addFlotOptions(this.flotOptions, this.panel);
// this.eventManager.addFlotEvents(this.annotations, this.flotOptions); // this.eventManager.addFlotEvents(this.annotations, this.flotOptions);
this._analyticController.updateFlotEvents(this.contextSrv.isEditor, this.flotOptions); this._analyticController.updateFlotEvents(this.contextSrv.isEditor, this.flotOptions);

5
src/models/analytic_unit.ts

@ -32,8 +32,6 @@ export class AnalyticUnit {
private _error: string; private _error: string;
private _metric: Metric; private _metric: Metric;
private _alertEnabled?: boolean;
constructor(private _panelObject?: any) { constructor(private _panelObject?: any) {
if(_panelObject === undefined) { if(_panelObject === undefined) {
this._panelObject = {}; this._panelObject = {};
@ -126,9 +124,6 @@ export class AnalyticUnit {
get panelObject() { return this._panelObject; } get panelObject() { return this._panelObject; }
get alertEnabled(): boolean { return this._alertEnabled; }
set alertEnabled(value) { this._alertEnabled = value;}
} }
export class AnalyticUnitsSet { export class AnalyticUnitsSet {

19
src/module.ts

@ -1,5 +1,4 @@
import './series_overrides_ctrl'; import './series_overrides_ctrl';
import './thresholds_form';
import template from './template'; import template from './template';
@ -15,10 +14,10 @@ import { PanelInfo } from './models/info';
import { axesEditorComponent } from './axes_editor'; import { axesEditorComponent } from './axes_editor';
import { MetricsPanelCtrl, alertTab } from 'grafana/app/plugins/sdk'; import { MetricsPanelCtrl } from 'grafana/app/plugins/sdk';
import { appEvents } from 'grafana/app/core/core' import { appEvents } from 'grafana/app/core/core'
import { BackendSrv } from 'grafana/app/core/services/backend_srv'; import { BackendSrv } from 'grafana/app/core/services/backend_srv';
import { AlertSrv } from 'grafana/app/core/services/alert_srv'; import { AlertSrv } from 'grafana/app/core/services/alert_srv'
import config from 'grafana/app/core/config'; import config from 'grafana/app/core/config';
@ -48,7 +47,6 @@ class GraphCtrl extends MetricsPanelCtrl {
datasourceRequest: DatasourceRequest; datasourceRequest: DatasourceRequest;
anomalyTypes = []; // TODO: remove it later. Only for alert tab
analyticsController: AnalyticController; analyticsController: AnalyticController;
_graphRenderer: GraphRenderer; _graphRenderer: GraphRenderer;
@ -173,7 +171,6 @@ class GraphCtrl extends MetricsPanelCtrl {
this.runBackendConnectivityCheck(); this.runBackendConnectivityCheck();
this.analyticsController = new AnalyticController(this.panel, this.analyticService, this.events); this.analyticsController = new AnalyticController(this.panel, this.analyticService, this.events);
this.anomalyTypes = this.panel.anomalyTypes;
keybindingSrv.bind('d', this.onDKey.bind(this)); keybindingSrv.bind('d', this.onDKey.bind(this));
this.events.on('render', this.onRender.bind(this)); this.events.on('render', this.onRender.bind(this));
@ -182,9 +179,7 @@ class GraphCtrl extends MetricsPanelCtrl {
this.events.on('data-snapshot-load', this.onDataSnapshotLoad.bind(this)); this.events.on('data-snapshot-load', this.onDataSnapshotLoad.bind(this));
this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
this.events.on('init-panel-actions', this.onInitPanelActions.bind(this)); this.events.on('init-panel-actions', this.onInitPanelActions.bind(this));
this.events.on('anomaly-type-alert-change', () => {
this.$scope.$digest()
});
this.events.on('analytic-unit-status-change', async (analyticUnit: AnalyticUnit) => { this.events.on('analytic-unit-status-change', async (analyticUnit: AnalyticUnit) => {
if(analyticUnit === undefined) { if(analyticUnit === undefined) {
throw new Error('analyticUnit is undefined'); throw new Error('analyticUnit is undefined');
@ -264,10 +259,6 @@ class GraphCtrl extends MetricsPanelCtrl {
this.addEditorTab('Display', `${partialPath}/tab_display.html`, 5); this.addEditorTab('Display', `${partialPath}/tab_display.html`, 5);
this.addEditorTab('Plugin info', `${partialPath}/tab_info.html`, 6); this.addEditorTab('Plugin info', `${partialPath}/tab_info.html`, 6);
if(config.alertingEnabled) {
this.addEditorTab('Alert', alertTab, 6);
}
this.subTabIndex = 0; this.subTabIndex = 0;
} }
@ -607,10 +598,6 @@ class GraphCtrl extends MetricsPanelCtrl {
this.refresh(); this.refresh();
} }
onAnomalyAlertChange(anomalyType: AnalyticUnit) {
this.analyticsController.toggleAlertEnabled(anomalyType);
}
onToggleVisibility(id: AnalyticUnitId) { onToggleVisibility(id: AnalyticUnitId) {
this.analyticsController.toggleVisibility(id); this.analyticsController.toggleVisibility(id);
this.render(); this.render();

18
src/partials/tab_analytics.html

@ -57,24 +57,6 @@
</a> </a>
</label> </label>
<!-- <label class="gf-form-label"> Alerts: </label>
<label
class="gf-form-label text-center"
style="width: 4rem"
ng-if="analyticUnit.alertEnabled === undefined"
bs-tooltip="'Alarting status isn`t available. Wait please.'"
>
<i class="fa fa-spinner fa-spin"></i>
</label> -->
<gf-form-switch
ng-if="analyticUnit.alertEnabled !== undefined"
on-change="ctrl.onAnomalyAlertChange(analyticUnit)"
checked="analyticUnit.alertEnabled"
style="height: 36px;"
/>
<label class="gf-form-label"> <label class="gf-form-label">
<a <a
ng-if="analyticUnit.visible" ng-if="analyticUnit.visible"

18
src/services/analytic_service.ts

@ -113,24 +113,6 @@ export class AnalyticService {
} }
} }
async getAlertEnabled(id: AnalyticUnitId): Promise<boolean> {
if(id === undefined) {
throw new Error('id is undefined');
}
var data = await this.get('/alerts', { id });
if(data.enabled === undefined) {
throw new Error('Server didn`t return "enabled"');
}
return data.enabled as boolean;
}
async setAlertEnabled(id: AnalyticUnitId, enabled: boolean): Promise<void> {
if(id === undefined) {
throw new Error('id is undefined');
}
return await this.post('/alerts', { id, enabled });
}
async getServerInfo(): Promise<ServerInfo> { async getServerInfo(): Promise<ServerInfo> {
let data = await this.get('/'); let data = await this.get('/');
return { return {

237
src/threshold_manager.ts

@ -1,237 +0,0 @@
import 'jquery';
import _ from 'lodash';
export class ThresholdManager {
plot: any;
placeholder: any;
height: any;
thresholds: any;
needsCleanup: boolean;
hasSecondYAxis: any;
constructor(private panelCtrl) {}
getHandleHtml(handleIndex, model, valueStr) {
var stateClass = model.colorMode;
if (model.colorMode === 'custom') {
stateClass = 'critical';
}
return `
<div class="alert-handle-wrapper alert-handle-wrapper--T${handleIndex}">
<div class="alert-handle-line alert-handle-line--${stateClass}">
</div>
<div class="alert-handle" data-handle-index="${handleIndex}">
<i class="icon-gf icon-gf-${stateClass} alert-state-${stateClass}"></i>
<span class="alert-handle-value">${valueStr}<i class="alert-handle-grip"></i></span>
</div>
</div>`;
}
initDragging(evt) {
var handleElem = $(evt.currentTarget).parents('.alert-handle-wrapper');
var handleIndex = $(evt.currentTarget).data('handleIndex');
var lastY = null;
var posTop;
var plot = this.plot;
var panelCtrl = this.panelCtrl;
var model = this.thresholds[handleIndex];
function dragging(evt) {
if (lastY === null) {
lastY = evt.clientY;
} else {
var diff = evt.clientY - lastY;
posTop = posTop + diff;
lastY = evt.clientY;
handleElem.css({ top: posTop + diff });
}
}
function stopped() {
// calculate graph level
var graphValue = plot.c2p({ left: 0, top: posTop }).y;
graphValue = parseInt(graphValue.toFixed(0));
model.value = graphValue;
handleElem.off('mousemove', dragging);
handleElem.off('mouseup', dragging);
handleElem.off('mouseleave', dragging);
// trigger digest and render
panelCtrl.$scope.$apply(function() {
panelCtrl.render();
panelCtrl.events.emit('threshold-changed', {
threshold: model,
handleIndex: handleIndex,
});
});
}
lastY = null;
posTop = handleElem.position().top;
handleElem.on('mousemove', dragging);
handleElem.on('mouseup', stopped);
handleElem.on('mouseleave', stopped);
}
cleanUp() {
this.placeholder.find('.alert-handle-wrapper').remove();
this.needsCleanup = false;
}
renderHandle(handleIndex, defaultHandleTopPos) {
var model = this.thresholds[handleIndex];
var value = model.value;
var valueStr = value;
var handleTopPos = 0;
// handle no value
if (!_.isNumber(value)) {
valueStr = '';
handleTopPos = defaultHandleTopPos;
} else {
var valueCanvasPos = this.plot.p2c({ x: 0, y: value });
handleTopPos = Math.round(Math.min(Math.max(valueCanvasPos.top, 0), this.height) - 6);
}
var handleElem = $(this.getHandleHtml(handleIndex, model, valueStr));
this.placeholder.append(handleElem);
handleElem.toggleClass('alert-handle-wrapper--no-value', valueStr === '');
handleElem.css({ top: handleTopPos });
}
shouldDrawHandles() {
return !this.hasSecondYAxis && this.panelCtrl.editingThresholds && this.panelCtrl.panel.thresholds.length > 0;
}
prepare(elem, data) {
this.hasSecondYAxis = false;
for (var i = 0; i < data.length; i++) {
if (data[i].yaxis > 1) {
this.hasSecondYAxis = true;
break;
}
}
if (this.shouldDrawHandles()) {
var thresholdMargin = this.panelCtrl.panel.thresholds.length > 1 ? '220px' : '110px';
elem.css('margin-right', thresholdMargin);
} else if (this.needsCleanup) {
elem.css('margin-right', '0');
}
}
draw(plot) {
this.thresholds = this.panelCtrl.panel.thresholds;
this.plot = plot;
this.placeholder = plot.getPlaceholder();
if (this.needsCleanup) {
this.cleanUp();
}
if (!this.shouldDrawHandles()) {
return;
}
this.height = plot.height();
if (this.thresholds.length > 0) {
this.renderHandle(0, 10);
}
if (this.thresholds.length > 1) {
this.renderHandle(1, this.height - 30);
}
this.placeholder.off('mousedown', '.alert-handle');
this.placeholder.on('mousedown', '.alert-handle', this.initDragging.bind(this));
this.needsCleanup = true;
}
addFlotOptions(options, panel) {
if (!panel.thresholds || panel.thresholds.length === 0) {
return;
}
var gtLimit = Infinity;
var ltLimit = -Infinity;
var i, threshold, other;
for (i = 0; i < panel.thresholds.length; i++) {
threshold = panel.thresholds[i];
if (!_.isNumber(threshold.value)) {
continue;
}
var limit;
switch (threshold.op) {
case 'gt': {
limit = gtLimit;
// if next threshold is less then op and greater value, then use that as limit
if (panel.thresholds.length > i + 1) {
other = panel.thresholds[i + 1];
if (other.value > threshold.value) {
limit = other.value;
ltLimit = limit;
}
}
break;
}
case 'lt': {
limit = ltLimit;
// if next threshold is less then op and greater value, then use that as limit
if (panel.thresholds.length > i + 1) {
other = panel.thresholds[i + 1];
if (other.value < threshold.value) {
limit = other.value;
gtLimit = limit;
}
}
break;
}
}
var fillColor, lineColor;
switch (threshold.colorMode) {
case 'critical': {
fillColor = 'rgba(234, 112, 112, 0.12)';
lineColor = 'rgba(237, 46, 24, 0.60)';
break;
}
case 'warning': {
fillColor = 'rgba(235, 138, 14, 0.12)';
lineColor = 'rgba(247, 149, 32, 0.60)';
break;
}
case 'ok': {
fillColor = 'rgba(11, 237, 50, 0.090)';
lineColor = 'rgba(6,163,69, 0.60)';
break;
}
case 'custom': {
fillColor = threshold.fillColor;
lineColor = threshold.lineColor;
break;
}
}
// fill
if (threshold.fill) {
options.grid.markings.push({
yaxis: { from: threshold.value, to: limit },
color: fillColor,
});
}
if (threshold.line) {
options.grid.markings.push({
yaxis: { from: threshold.value, to: threshold.value },
color: lineColor,
});
}
}
}
}

142
src/thresholds_form.ts

@ -1,142 +0,0 @@
import coreModule from 'grafana/app/core/core_module';
export class ThresholdFormCtrl {
panelCtrl: any;
panel: any;
disabled: boolean;
/** @ngInject */
constructor($scope) {
this.panel = this.panelCtrl.panel;
if (this.panel.alert) {
this.disabled = true;
}
var unbindDestroy = $scope.$on('$destroy', () => {
this.panelCtrl.editingThresholds = false;
this.panelCtrl.render();
unbindDestroy();
});
this.panelCtrl.editingThresholds = true;
}
addThreshold() {
this.panel.thresholds.push({
value: undefined,
colorMode: 'critical',
op: 'gt',
fill: true,
line: true,
});
this.panelCtrl.render();
}
removeThreshold(index) {
this.panel.thresholds.splice(index, 1);
this.panelCtrl.render();
}
render() {
this.panelCtrl.render();
}
onFillColorChange(index) {
return newColor => {
this.panel.thresholds[index].fillColor = newColor;
this.render();
};
}
onLineColorChange(index) {
return newColor => {
this.panel.thresholds[index].lineColor = newColor;
this.render();
};
}
}
var template = `
<div class="gf-form-group">
<h5>Thresholds</h5>
<p class="muted" ng-show="ctrl.disabled">
Visual thresholds options <strong>disabled.</strong>
Visit the Alert tab update your thresholds. <br>
To re-enable thresholds, the alert rule must be deleted from this panel.
</p>
<div ng-class="{'thresholds-form-disabled': ctrl.disabled}">
<div class="gf-form-inline" ng-repeat="threshold in ctrl.panel.thresholds">
<div class="gf-form">
<label class="gf-form-label">T{{$index+1}}</label>
</div>
<div class="gf-form">
<div class="gf-form-select-wrapper">
<select class="gf-form-input" ng-model="threshold.op"
ng-options="f for f in ['gt', 'lt']" ng-change="ctrl.render()" ng-disabled="ctrl.disabled"></select>
</div>
<input type="number" ng-model="threshold.value" class="gf-form-input width-8"
ng-change="ctrl.render()" placeholder="value" ng-disabled="ctrl.disabled">
</div>
<div class="gf-form">
<label class="gf-form-label">Color</label>
<div class="gf-form-select-wrapper">
<select class="gf-form-input" ng-model="threshold.colorMode"
ng-options="f for f in ['custom', 'critical', 'warning', 'ok']" ng-change="ctrl.render()" ng-disabled="ctrl.disabled">
</select>
</div>
</div>
<gf-form-switch class="gf-form" label="Fill" checked="threshold.fill"
on-change="ctrl.render()" ng-disabled="ctrl.disabled"></gf-form-switch>
<div class="gf-form" ng-if="threshold.fill && threshold.colorMode === 'custom'">
<label class="gf-form-label">Fill color</label>
<span class="gf-form-label">
<color-picker color="threshold.fillColor" onChange="ctrl.onFillColorChange($index)"></color-picker>
</span>
</div>
<gf-form-switch class="gf-form" label="Line" checked="threshold.line"
on-change="ctrl.render()" ng-disabled="ctrl.disabled"></gf-form-switch>
<div class="gf-form" ng-if="threshold.line && threshold.colorMode === 'custom'">
<label class="gf-form-label">Line color</label>
<span class="gf-form-label">
<color-picker color="threshold.lineColor" onChange="ctrl.onLineColorChange($index)"></color-picker>
</span>
</div>
<div class="gf-form">
<label class="gf-form-label">
<a class="pointer" ng-click="ctrl.removeThreshold($index)" ng-disabled="ctrl.disabled">
<i class="fa fa-trash"></i>
</a>
</label>
</div>
</div>
<div class="gf-form-button-row">
<button class="btn btn-inverse" ng-click="ctrl.addThreshold()" ng-disabled="ctrl.disabled">
<i class="fa fa-plus"></i>&nbsp;Add Threshold
</button>
</div>
</div>
</div>
`;
coreModule.directive('hasticGraphThresholdForm', function() {
return {
restrict: 'E',
template: template,
controller: ThresholdFormCtrl,
bindToController: true,
controllerAs: 'ctrl',
scope: {
panelCtrl: '=',
},
};
});
Loading…
Cancel
Save