From 9e91aacd34bf94d54ae78d38a4d96119b23e8101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 21 Jul 2016 10:29:11 +0200 Subject: [PATCH] feat(alerting): progress on testing alerts --- pkg/api/alerting.go | 20 +++++++++++++--- pkg/api/api.go | 2 +- pkg/api/dtos/alerting.go | 12 +++++++--- pkg/services/alerting/engine.go | 3 +-- pkg/services/alerting/handler.go | 1 + pkg/services/alerting/models.go | 4 ++++ pkg/services/alerting/test_rule.go | 24 ++++++++++--------- pkg/tsdb/graphite/graphite.go | 8 +++++-- .../app/plugins/panel/graph/alert_tab_ctrl.ts | 20 ++++++++++++++-- .../panel/graph/partials/tab_alerting.html | 8 +++++++ 10 files changed, 78 insertions(+), 24 deletions(-) diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index f9605312574..263881df4f4 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -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 diff --git a/pkg/api/api.go b/pkg/api/api.go index 5e2d457b1e3..f23d3f1a2f8 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -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)) diff --git a/pkg/api/dtos/alerting.go b/pkg/api/dtos/alerting.go index e9c57b57ce2..3421616e484 100644 --- a/pkg/api/dtos/alerting.go +++ b/pkg/api/dtos/alerting.go @@ -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"` } diff --git a/pkg/services/alerting/engine.go b/pkg/services/alerting/engine.go index 799f6cb2b58..e1a696a180e 100644 --- a/pkg/services/alerting/engine.go +++ b/pkg/services/alerting/engine.go @@ -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 } diff --git a/pkg/services/alerting/handler.go b/pkg/services/alerting/handler.go index 75a584fda81..58fa9e024d6 100644 --- a/pkg/services/alerting/handler.go +++ b/pkg/services/alerting/handler.go @@ -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 { diff --git a/pkg/services/alerting/models.go b/pkg/services/alerting/models.go index 58c025ffeb4..01afdf176d6 100644 --- a/pkg/services/alerting/models.go +++ b/pkg/services/alerting/models.go @@ -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 diff --git a/pkg/services/alerting/test_rule.go b/pkg/services/alerting/test_rule.go index 0d51748a5da..69de5d9a44b 100644 --- a/pkg/services/alerting/test_rule.go +++ b/pkg/services/alerting/test_rule.go @@ -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 { diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index b8f3e16a362..2a68b23df71 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -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) } diff --git a/public/app/plugins/panel/graph/alert_tab_ctrl.ts b/public/app/plugins/panel/graph/alert_tab_ctrl.ts index 211e08675dd..d69a496439f 100644 --- a/public/app/plugins/panel/graph/alert_tab_ctrl.ts +++ b/public/app/plugins/panel/graph/alert_tab_ctrl.ts @@ -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 */ diff --git a/public/app/plugins/panel/graph/partials/tab_alerting.html b/public/app/plugins/panel/graph/partials/tab_alerting.html index 377c6aad3ea..41fea0b0f0d 100644 --- a/public/app/plugins/panel/graph/partials/tab_alerting.html +++ b/public/app/plugins/panel/graph/partials/tab_alerting.html @@ -116,6 +116,10 @@ + + @@ -123,6 +127,10 @@ +
+ Evaluating rule +
+