mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(alerting): making progress on alerting list, #5784
This commit is contained in:
parent
9f29c54170
commit
f934081bcb
@ -43,13 +43,16 @@ func GetAlerts(c *middleware.Context) Response {
|
||||
for _, alert := range query.Result {
|
||||
dashboardIds = append(dashboardIds, alert.DashboardId)
|
||||
alertDTOs = append(alertDTOs, &dtos.AlertRule{
|
||||
Id: alert.Id,
|
||||
DashboardId: alert.DashboardId,
|
||||
PanelId: alert.PanelId,
|
||||
Name: alert.Name,
|
||||
Message: alert.Message,
|
||||
State: alert.State,
|
||||
Severity: alert.Severity,
|
||||
Id: alert.Id,
|
||||
DashboardId: alert.DashboardId,
|
||||
PanelId: alert.PanelId,
|
||||
Name: alert.Name,
|
||||
Message: alert.Message,
|
||||
State: alert.State,
|
||||
Severity: alert.Severity,
|
||||
EvalDate: alert.EvalDate,
|
||||
NewStateDate: alert.NewStateDate,
|
||||
ExecutionError: alert.ExecutionError,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -8,15 +8,17 @@ import (
|
||||
)
|
||||
|
||||
type AlertRule struct {
|
||||
Id int64 `json:"id"`
|
||||
DashboardId int64 `json:"dashboardId"`
|
||||
PanelId int64 `json:"panelId"`
|
||||
Name string `json:"name"`
|
||||
Message string `json:"message"`
|
||||
State m.AlertStateType `json:"state"`
|
||||
Severity m.AlertSeverityType `json:"severity"`
|
||||
|
||||
DashbboardUri string `json:"dashboardUri"`
|
||||
Id int64 `json:"id"`
|
||||
DashboardId int64 `json:"dashboardId"`
|
||||
PanelId int64 `json:"panelId"`
|
||||
Name string `json:"name"`
|
||||
Message string `json:"message"`
|
||||
State m.AlertStateType `json:"state"`
|
||||
Severity m.AlertSeverityType `json:"severity"`
|
||||
NewStateDate time.Time `json:"newStateDate"`
|
||||
EvalDate time.Time `json:"evalDate"`
|
||||
ExecutionError string `json:"executionError"`
|
||||
DashbboardUri string `json:"dashboardUri"`
|
||||
}
|
||||
|
||||
type AlertNotification struct {
|
||||
|
@ -93,14 +93,14 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
|
||||
|
||||
if setting.AlertingEnabled && (c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR) {
|
||||
alertChildNavs := []*dtos.NavLink{
|
||||
{Text: "Home", Url: setting.AppSubUrl + "/alerting"},
|
||||
{Text: "Alert List", Url: setting.AppSubUrl + "/alerting/list"},
|
||||
{Text: "Notifications", Url: setting.AppSubUrl + "/alerting/notifications"},
|
||||
}
|
||||
|
||||
data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
|
||||
Text: "Alerting",
|
||||
Icon: "icon-gf icon-gf-monitoring",
|
||||
Url: setting.AppSubUrl + "/alerting",
|
||||
Url: setting.AppSubUrl + "/alerting/list",
|
||||
Children: alertChildNavs,
|
||||
})
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ func (s AlertSeverityType) IsValid() bool {
|
||||
|
||||
type Alert struct {
|
||||
Id int64
|
||||
Version int64
|
||||
OrgId int64
|
||||
DashboardId int64
|
||||
PanelId int64
|
||||
@ -45,11 +46,10 @@ type Alert struct {
|
||||
ExecutionError string
|
||||
Frequency int64
|
||||
|
||||
LastEvalData *simplejson.Json
|
||||
LastEvalTime time.Time
|
||||
|
||||
CreatedBy int64
|
||||
UpdatedBy int64
|
||||
EvalData *simplejson.Json
|
||||
EvalDate time.Time
|
||||
NewStateDate time.Time
|
||||
StateChanges int
|
||||
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
|
@ -161,8 +161,6 @@ func upsertAlerts(existingAlerts []*m.Alert, cmd *m.SaveAlertsCommand, sess *xor
|
||||
alert.Updated = time.Now()
|
||||
alert.Created = time.Now()
|
||||
alert.State = m.AlertStatePending
|
||||
alert.CreatedBy = cmd.UserId
|
||||
alert.UpdatedBy = cmd.UserId
|
||||
|
||||
_, err := sess.Insert(alert)
|
||||
if err != nil {
|
||||
@ -222,8 +220,10 @@ func SetAlertState(cmd *m.SetAlertStateCommand) error {
|
||||
}
|
||||
|
||||
alert.State = cmd.State
|
||||
sess.Id(alert.Id).Update(&alert)
|
||||
alert.StateChanges += 1
|
||||
alert.NewStateDate = time.Now()
|
||||
|
||||
sess.Id(alert.Id).Update(&alert)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ func addAlertMigrations(mg *Migrator) {
|
||||
Name: "alert",
|
||||
Columns: []*Column{
|
||||
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||
{Name: "version", Type: DB_BigInt, Nullable: false},
|
||||
{Name: "dashboard_id", Type: DB_BigInt, Nullable: false},
|
||||
{Name: "panel_id", Type: DB_BigInt, Nullable: false},
|
||||
{Name: "org_id", Type: DB_BigInt, Nullable: false},
|
||||
@ -23,12 +24,12 @@ func addAlertMigrations(mg *Migrator) {
|
||||
{Name: "paused", Type: DB_Bool, Nullable: false},
|
||||
{Name: "silenced", Type: DB_Bool, Nullable: false},
|
||||
{Name: "execution_error", Type: DB_Text, Nullable: false},
|
||||
{Name: "last_eval_data", Type: DB_Text, Nullable: false},
|
||||
{Name: "last_eval_time", Type: DB_DateTime, Nullable: false},
|
||||
{Name: "eval_data", Type: DB_Text, Nullable: true},
|
||||
{Name: "eval_date", Type: DB_DateTime, Nullable: true},
|
||||
{Name: "new_state_date", Type: DB_DateTime, Nullable: false},
|
||||
{Name: "state_changes", Type: DB_Int, Nullable: false},
|
||||
{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
{Name: "updated_by", Type: DB_BigInt, Nullable: false},
|
||||
{Name: "created_by", Type: DB_BigInt, Nullable: false},
|
||||
},
|
||||
Indices: []*Index{
|
||||
{Cols: []string{"org_id", "id"}, Type: IndexType},
|
||||
|
@ -32,6 +32,7 @@ func addAnnotationMig(mg *Migrator) {
|
||||
|
||||
// create indices
|
||||
mg.AddMigration("add index annotation org_id & alert_id ", NewAddIndexMigration(table, table.Indices[0]))
|
||||
|
||||
mg.AddMigration("add index annotation org_id & type", NewAddIndexMigration(table, table.Indices[1]))
|
||||
mg.AddMigration("add index annotation timestamp", NewAddIndexMigration(table, table.Indices[2]))
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
|
||||
controllerAs: 'ctrl',
|
||||
templateUrl: 'public/app/features/styleguide/styleguide.html',
|
||||
})
|
||||
.when('/alerting', {
|
||||
.when('/alerting/list', {
|
||||
templateUrl: 'public/app/features/alerting/partials/alert_list.html',
|
||||
controller: 'AlertListCtrl',
|
||||
controllerAs: 'ctrl',
|
||||
|
@ -1,14 +1,5 @@
|
||||
///<reference path="../../headers/common.d.ts" />
|
||||
|
||||
var alertSeverityIconMap = {
|
||||
"ok": "icon-gf-online alert-icon-online",
|
||||
"warning": "icon-gf-warn alert-icon-warn",
|
||||
"critical": "icon-gf-critical alert-icon-critical",
|
||||
};
|
||||
|
||||
function getSeverityIconClass(alertState) {
|
||||
return alertSeverityIconMap[alertState];
|
||||
}
|
||||
|
||||
import {
|
||||
QueryPartDef,
|
||||
@ -50,14 +41,28 @@ function createReducerPart(model) {
|
||||
return new QueryPart(model, def);
|
||||
}
|
||||
|
||||
var severityLevels = [
|
||||
{text: 'Critical', value: 'critical'},
|
||||
{text: 'Warning', value: 'warning'},
|
||||
];
|
||||
var severityLevels = {
|
||||
'critical': {text: 'Critical', iconClass: 'icon-gf-critical alert-icon-critical'},
|
||||
'warning': {text: 'Warning', iconClass: 'icon-gf-warn alert-icon-warn'},
|
||||
};
|
||||
|
||||
function getStateDisplayModel(state, severity) {
|
||||
var model = {
|
||||
text: 'OK',
|
||||
iconClass: 'icon-gf-online alert-icon-online'
|
||||
};
|
||||
|
||||
if (state === 'firing') {
|
||||
model.text = severityLevels[severity].text;
|
||||
model.iconClass = severityLevels[severity].iconClass;
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
export default {
|
||||
alertQueryDef: alertQueryDef,
|
||||
getSeverityIconClass: getSeverityIconClass,
|
||||
getStateDisplayModel: getStateDisplayModel,
|
||||
conditionTypes: conditionTypes,
|
||||
evalFunctions: evalFunctions,
|
||||
severityLevels: severityLevels,
|
||||
|
@ -3,23 +3,20 @@
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import coreModule from '../../core/core_module';
|
||||
import config from 'app/core/config';
|
||||
import moment from 'moment';
|
||||
import alertDef from './alert_def';
|
||||
|
||||
export class AlertListCtrl {
|
||||
|
||||
alerts: any;
|
||||
filter = {
|
||||
ok: false,
|
||||
warn: false,
|
||||
critical: false,
|
||||
acknowleged: false
|
||||
filters = {
|
||||
state: 'OK'
|
||||
};
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private backendSrv, private $route) {
|
||||
_.each($route.current.params.state, state => {
|
||||
this.filter[state.toLowerCase()] = true;
|
||||
this.filters[state.toLowerCase()] = true;
|
||||
});
|
||||
|
||||
this.loadAlerts();
|
||||
@ -27,10 +24,6 @@ export class AlertListCtrl {
|
||||
|
||||
updateFilter() {
|
||||
var stats = [];
|
||||
this.filter.ok && stats.push('OK');
|
||||
this.filter.warn && stats.push('Warn');
|
||||
this.filter.critical && stats.push('critical');
|
||||
|
||||
this.$route.current.params.state = stats;
|
||||
this.$route.updateParams();
|
||||
}
|
||||
@ -38,17 +31,14 @@ export class AlertListCtrl {
|
||||
loadAlerts() {
|
||||
var stats = [];
|
||||
|
||||
this.filter.ok && stats.push('OK');
|
||||
this.filter.warn && stats.push('Warn');
|
||||
this.filter.critical && stats.push('critical');
|
||||
|
||||
var params = {
|
||||
state: stats
|
||||
};
|
||||
|
||||
this.backendSrv.get('/api/alerts', params).then(result => {
|
||||
this.alerts = _.map(result, alert => {
|
||||
alert.severityClass = alertDef.getSeverityIconClass(alert.severity);
|
||||
alert.stateModel = alertDef.getStateDisplayModel(alert.state, alert.severity);
|
||||
alert.newStateDateAgo = moment(alert.newStateDate).fromNow().replace(" ago", "");
|
||||
return alert;
|
||||
});
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
import './alerts_ctrl';
|
||||
import './alert_list_ctrl';
|
||||
import './alert_log_ctrl';
|
||||
import './notifications_list_ctrl';
|
||||
import './notification_edit_ctrl';
|
||||
|
@ -6,38 +6,46 @@
|
||||
<h1>Alerting</h1>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<gf-form-switch class="gf-form" label="OK" label-class="width-5" checked="ctrl.filter.ok" on-change="ctrl.updateFilter()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Warn" label-class="width-5" checked="ctrl.filter.warn" on-change="ctrl.updateFilter()"></gf-form-switch>
|
||||
<gf-form-switch class="gf-form" label="Critical" label-class="width-5" checked="ctrl.filter.critical" on-change="ctrl.updateFilter()"></gf-form-switch>
|
||||
</div>
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label">Filter by state</label>
|
||||
<div class="gf-form-select-wrapper width-13">
|
||||
<select class="gf-form-input" ng-model="ctrl.filters.state" ng-options="f for f in ['OK', 'Warning', 'Critical']" ng-change="ctrl.severityChanged()">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="card-section card-list-layout-list">
|
||||
|
||||
<ol class="card-list" >
|
||||
<li class="card-item-wrapper" ng-repeat="alert in ctrl.alerts">
|
||||
<a class="card-item" href="dashboard/{{alert.dashboardUri}}?panelId={{alert.panelId}}&fullscreen&edit&tab=alert">
|
||||
<div class="card-item-header">
|
||||
<div class="card-item-type">ACTIVE</div>
|
||||
<div class="card-item-notice" ng-show="alert.executionError">
|
||||
<span>Execution Error</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-item-body">
|
||||
<div class="card-item-details">
|
||||
<div class="card-item-name">{{alert.name}}</div>
|
||||
<div class="card-item-sub-name">
|
||||
<div class="alert-list-state-line">
|
||||
<i class="icon-gf {{alert.stateModel.iconClass}}"></i>
|
||||
{{alert.stateModel.text}}
|
||||
for
|
||||
{{alert.newStateDateAgo}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<table class="grafana-options-table">
|
||||
<thead>
|
||||
<th style="min-width: 200px"><strong>Name</strong></th>
|
||||
<th style="width: 1%">State</th>
|
||||
<th style="width: 1%">Severity</th>
|
||||
<th style="width: 1%"></th>
|
||||
</thead>
|
||||
<tr ng-repeat="alert in ctrl.alerts">
|
||||
<td>
|
||||
<a href="dashboard/{{alert.dashboardUri}}?panelId={{alert.panelId}}&fullscreen&edit&editorTab=Alerting">
|
||||
{{alert.name}}
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
{{alert.state}}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
{{alert.severity}}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="dashboard/{{alert.dashboardUri}}?panelId={{alert.panelId}}&fullscreen&edit&editorTab=Alerting" class="btn btn-inverse btn-small">
|
||||
<i class="fa fa-edit"></i>
|
||||
edit
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">Severity</span>
|
||||
<div class="gf-form-select-wrapper width-13">
|
||||
<select class="gf-form-input" ng-model="ctrl.alert.severity" ng-options="f.value as f.text for f in ctrl.severityLevels" ng-change="ctrl.severityChanged()">
|
||||
<select class="gf-form-input" ng-model="ctrl.alert.severity" ng-options="key as value.text for (key, value) in ctrl.severityLevels" ng-change="ctrl.severityChanged()">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,10 +5,6 @@
|
||||
<div class="page-header">
|
||||
<h1>Plugins</h1>
|
||||
|
||||
<!-- <a class="btn btn-inverse" href="https://grafana.net/plugins?utm_source=grafana_plugin_list" target="_blank"> -->
|
||||
<!-- Explore plugins on Grafana.net -->
|
||||
<!-- </a> -->
|
||||
|
||||
<div class="page-header-tabs">
|
||||
<ul class="gf-tabs">
|
||||
<li class="gf-tabs-item">
|
||||
|
Loading…
Reference in New Issue
Block a user