feat(alerting): progress on testing alerts

This commit is contained in:
Torkel Ödegaard
2016-07-21 10:29:11 +02:00
parent fb636344a6
commit 9e91aacd34
10 changed files with 78 additions and 24 deletions

View File

@ -1,6 +1,8 @@
package api
import (
"fmt"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
@ -73,8 +75,8 @@ func GetAlerts(c *middleware.Context) Response {
}
// POST /api/alerts/test
func TestAlertRule(c *middleware.Context, dto dtos.TestAlertRuleCommand) Response {
backendCmd := alerting.TestAlertRuleCommand{
func AlertTest(c *middleware.Context, dto dtos.AlertTestCommand) Response {
backendCmd := alerting.AlertTestCommand{
OrgId: c.OrgId,
Dashboard: dto.Dashboard,
PanelId: dto.PanelId,
@ -84,7 +86,19 @@ func TestAlertRule(c *middleware.Context, dto dtos.TestAlertRuleCommand) Respons
return ApiError(500, "Failed to test rule", err)
}
return Json(200, backendCmd.Result)
res := backendCmd.Result
dtoRes := &dtos.AlertTestResult{
Triggered: res.Triggered,
}
if res.Error != nil {
dtoRes.Error = res.Error.Error()
}
dtoRes.Timing = fmt.Sprintf("%1.3fs", res.GetDurationSeconds())
return Json(200, dtoRes)
}
// GET /api/alerts/:id

View File

@ -246,7 +246,7 @@ func Register(r *macaron.Macaron) {
r.Get("/metrics", wrap(GetInternalMetrics))
r.Group("/alerts", func() {
r.Post("/test", bind(dtos.TestAlertRuleCommand{}), wrap(TestAlertRule))
r.Post("/test", bind(dtos.AlertTestCommand{}), wrap(AlertTest))
r.Get("/:alertId/states", wrap(GetAlertStates))
//r.Put("/:alertId/state", bind(m.UpdateAlertStateCommand{}), wrap(PutAlertState))
r.Get("/:alertId", ValidateOrgAlert, wrap(GetAlert))

View File

@ -34,7 +34,13 @@ type AlertNotificationDTO struct {
Updated time.Time `json:"updated"`
}
type TestAlertRuleCommand struct {
Dashboard *simplejson.Json `json:"dashboard"`
PanelId int64 `json:"panelId"`
type AlertTestCommand struct {
Dashboard *simplejson.Json `json:"dashboard" binding:"Required"`
PanelId int64 `json:"panelId" binding:"Required"`
}
type AlertTestResult struct {
Triggered bool `json:"triggerd"`
Timing string `json:"timing"`
Error string `json:"error"`
}

View File

@ -104,8 +104,7 @@ func (e *Engine) executeJob(job *AlertJob) {
close(resultChan)
e.log.Debug("Job Execution timeout", "alertRuleId", job.Rule.Id)
case result := <-resultChan:
duration := float64(result.EndTime.Nanosecond()-result.StartTime.Nanosecond()) / float64(1000000)
e.log.Debug("Job Execution done", "timeTakenMs", duration, "ruleId", job.Rule.Id)
e.log.Debug("Job Execution done", "timing", result.GetDurationSeconds(), "ruleId", job.Rule.Id)
e.resultQueue <- result
}

View File

@ -27,6 +27,7 @@ func (e *HandlerImpl) Execute(rule *AlertRule, resultQueue chan *AlertResultCont
func (e *HandlerImpl) eval(rule *AlertRule) *AlertResultContext {
result := &AlertResultContext{
StartTime: time.Now(),
Rule: rule,
}
for _, condition := range rule.Conditions {

View File

@ -36,6 +36,10 @@ type AlertResultContext struct {
Rule *AlertRule
}
func (a *AlertResultContext) GetDurationSeconds() float64 {
return float64(a.EndTime.Nanosecond()-a.StartTime.Nanosecond()) / float64(1000000000)
}
type AlertResultDetail struct {
Value float64
Metric string

View File

@ -9,7 +9,7 @@ import (
m "github.com/grafana/grafana/pkg/models"
)
type TestAlertRuleCommand struct {
type AlertTestCommand struct {
Dashboard *simplejson.Json
PanelId int64
OrgId int64
@ -18,24 +18,26 @@ type TestAlertRuleCommand struct {
}
func init() {
bus.AddHandler("alerting", handleTestAlertRuleCommand)
bus.AddHandler("alerting", handleAlertTestCommand)
}
func handleTestAlertRuleCommand(cmd *TestAlertRuleCommand) error {
func handleAlertTestCommand(cmd *AlertTestCommand) error {
dash, err := m.NewDashboardFromJson(cmd.Dashboard)
dash := m.NewDashboardFromJson(cmd.Dashboard)
extractor := NewDashAlertExtractor(dash, cmd.OrgId)
alerts, err := extractor.GetAlerts()
if err != nil {
return err
}
extractor := NewDashAlertExtractor(cmd.Dashboard)
rules, err := extractor.GetAlerts()
if err != nil {
return err
}
for _, alert := range alerts {
if alert.PanelId == cmd.PanelId {
rule, err := NewAlertRuleFromDBModel(alert)
if err != nil {
return err
}
for _, rule := range rules {
if rule.PanelId == cmd.PanelId {
if res, err := testAlertRule(rule); err != nil {
return err
} else {

View File

@ -31,14 +31,15 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC
result := &tsdb.BatchResult{}
params := url.Values{
"from": []string{formatTimeRange(context.TimeRange.From)},
"until": []string{context.TimeRange.To},
"from": []string{"-" + formatTimeRange(context.TimeRange.From)},
"until": []string{formatTimeRange(context.TimeRange.To)},
"format": []string{"json"},
"maxDataPoints": []string{"500"},
}
for _, query := range queries {
params["target"] = []string{query.Query}
glog.Debug("Graphite request", "query", query.Query)
}
client := http.Client{Timeout: time.Duration(10 * time.Second)}
@ -77,5 +78,8 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC
}
func formatTimeRange(input string) string {
if input == "now" {
return input
}
return strings.Replace(strings.Replace(input, "m", "min", -1), "M", "mon", -1)
}

View File

@ -29,6 +29,9 @@ export class AlertTabCtrl {
panel: any;
panelCtrl: any;
metricTargets;
testing: boolean;
testResult: any;
handlers = [{text: 'Grafana', value: 1}, {text: 'External', value: 0}];
conditionTypes = [
{text: 'Query', value: 'query'},
@ -47,9 +50,8 @@ export class AlertTabCtrl {
{text: 'Warning', value: 'warning'},
];
/** @ngInject */
constructor($scope, private $timeout) {
constructor($scope, private $timeout, private backendSrv, private dashboardSrv) {
this.panelCtrl = $scope.ctrl;
this.panel = this.panelCtrl.panel;
$scope.ctrl = this;
@ -138,6 +140,20 @@ export class AlertTabCtrl {
thresholdsUpdated() {
this.panelCtrl.render();
}
test() {
this.testing = true;
var payload = {
dashboard: this.dashboardSrv.getCurrent().getSaveModelClone(),
panelId: this.panelCtrl.panel.id,
};
this.backendSrv.post('/api/alerts/test', payload).then(res => {
this.testResult = res;
this.testing = false;
});
}
}
/** @ngInject */

View File

@ -116,6 +116,10 @@
</ul>
</div>
<button class="btn btn-inverse" ng-click="ctrl.test()">
Test Rule
</button>
<button class="btn btn-inverse" ng-click="ctrl.delete()">
Delete Alert
</button>
@ -123,6 +127,10 @@
</div>
</div>
<div class="gf-form-group" ng-if="ctrl.testing">
Evaluating rule <i class="fa fa-spinner fa-spin"></i>
</div>
<div class="gf-form-group" ng-if="!ctrl.alert.enabled">
<div class="gf-form-button-row">
<button class="btn btn-inverse" ng-click="ctrl.enable()">