瀏覽代碼

feat(alerting): progress on testing alerts

Torkel Ödegaard 9 年之前
父節點
當前提交
9e91aacd34

+ 17 - 3
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

+ 1 - 1
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))

+ 9 - 3
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"`
 }

+ 1 - 2
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
 	}
 

+ 1 - 0
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 {

+ 4 - 0
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

+ 13 - 11
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)
-	if err != nil {
-		return err
-	}
+	dash := m.NewDashboardFromJson(cmd.Dashboard)
 
-	extractor := NewDashAlertExtractor(cmd.Dashboard)
-	rules, err := extractor.GetAlerts()
+	extractor := NewDashAlertExtractor(dash, cmd.OrgId)
+	alerts, err := extractor.GetAlerts()
 	if err != nil {
 		return err
 	}
 
-	for _, rule := range rules {
-		if rule.PanelId == cmd.PanelId {
+	for _, alert := range alerts {
+		if alert.PanelId == cmd.PanelId {
+			rule, err := NewAlertRuleFromDBModel(alert)
+			if err != nil {
+				return err
+			}
+
 			if res, err := testAlertRule(rule); err != nil {
 				return err
 			} else {

+ 6 - 2
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)
 }

+ 18 - 2
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 */

+ 8 - 0
public/app/plugins/panel/graph/partials/tab_alerting.html

@@ -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()">