Browse Source

feat(alerting): more output when testing alert

Torkel Ödegaard 9 years ago
parent
commit
783d697529

+ 5 - 0
pkg/api/dtos/alerting.go

@@ -50,3 +50,8 @@ type AlertTestResultLog struct {
 	Message string      `json:"message"`
 	Message string      `json:"message"`
 	Data    interface{} `json:"data"`
 	Data    interface{} `json:"data"`
 }
 }
+
+type AlertEvent struct {
+	Metric string  `json:"metric"`
+	Value  float64 `json:"value"`
+}

+ 4 - 5
pkg/models/alert_state.go

@@ -29,11 +29,10 @@ func (this *UpdateAlertStateCommand) IsValidState() bool {
 // Commands
 // Commands
 
 
 type UpdateAlertStateCommand struct {
 type UpdateAlertStateCommand struct {
-	AlertId         int64            `json:"alertId" binding:"Required"`
-	OrgId           int64            `json:"orgId" binding:"Required"`
-	State           string           `json:"state" binding:"Required"`
-	Info            string           `json:"info"`
-	TriggeredAlerts *simplejson.Json `json:"triggeredAlerts"`
+	AlertId int64  `json:"alertId" binding:"Required"`
+	OrgId   int64  `json:"orgId" binding:"Required"`
+	State   string `json:"state" binding:"Required"`
+	Info    string `json:"info"`
 
 
 	Result *Alert
 	Result *Alert
 }
 }

+ 2 - 2
pkg/services/alerting/alert_rule.go

@@ -70,11 +70,11 @@ func NewAlertRuleFromDBModel(ruleDef *m.Alert) (*AlertRule, error) {
 		}
 		}
 	}
 	}
 
 
-	for _, condition := range ruleDef.Settings.Get("conditions").MustArray() {
+	for index, condition := range ruleDef.Settings.Get("conditions").MustArray() {
 		conditionModel := simplejson.NewFromAny(condition)
 		conditionModel := simplejson.NewFromAny(condition)
 		switch conditionModel.Get("type").MustString() {
 		switch conditionModel.Get("type").MustString() {
 		case "query":
 		case "query":
-			queryCondition, err := NewQueryCondition(conditionModel)
+			queryCondition, err := NewQueryCondition(conditionModel, index)
 			if err != nil {
 			if err != nil {
 				return nil, err
 				return nil, err
 			}
 			}

+ 15 - 2
pkg/services/alerting/conditions.go

@@ -11,6 +11,7 @@ import (
 )
 )
 
 
 type QueryCondition struct {
 type QueryCondition struct {
+	Index         int
 	Query         AlertQuery
 	Query         AlertQuery
 	Reducer       QueryReducer
 	Reducer       QueryReducer
 	Evaluator     AlertEvaluator
 	Evaluator     AlertEvaluator
@@ -27,7 +28,18 @@ func (c *QueryCondition) Eval(context *AlertResultContext) {
 	for _, series := range seriesList {
 	for _, series := range seriesList {
 		reducedValue := c.Reducer.Reduce(series)
 		reducedValue := c.Reducer.Reduce(series)
 		pass := c.Evaluator.Eval(series, reducedValue)
 		pass := c.Evaluator.Eval(series, reducedValue)
+
+		if context.IsTestRun {
+			context.Logs = append(context.Logs, &AlertResultLogEntry{
+				Message: fmt.Sprintf("Condition[%d]: Eval: %v, Metric: %s, Value: %1.3f", c.Index, pass, series.Name, reducedValue),
+			})
+		}
+
 		if pass {
 		if pass {
+			context.Events = append(context.Events, &AlertEvent{
+				Metric: series.Name,
+				Value:  reducedValue,
+			})
 			context.Triggered = true
 			context.Triggered = true
 			break
 			break
 		}
 		}
@@ -61,7 +73,7 @@ func (c *QueryCondition) executeQuery(context *AlertResultContext) (tsdb.TimeSer
 
 
 		if context.IsTestRun {
 		if context.IsTestRun {
 			context.Logs = append(context.Logs, &AlertResultLogEntry{
 			context.Logs = append(context.Logs, &AlertResultLogEntry{
-				Message: "Query Condition Query Result",
+				Message: fmt.Sprintf("Condition[%d]: Query Result", c.Index),
 				Data:    v.Series,
 				Data:    v.Series,
 			})
 			})
 		}
 		}
@@ -93,8 +105,9 @@ func (c *QueryCondition) getRequestForAlertRule(datasource *m.DataSource) *tsdb.
 	return req
 	return req
 }
 }
 
 
-func NewQueryCondition(model *simplejson.Json) (*QueryCondition, error) {
+func NewQueryCondition(model *simplejson.Json, index int) (*QueryCondition, error) {
 	condition := QueryCondition{}
 	condition := QueryCondition{}
+	condition.Index = index
 	condition.HandleRequest = tsdb.HandleRequest
 	condition.HandleRequest = tsdb.HandleRequest
 
 
 	queryJson := model.Get("query")
 	queryJson := model.Get("query")

+ 1 - 1
pkg/services/alerting/conditions_test.go

@@ -60,7 +60,7 @@ func (ctx *queryConditionTestContext) exec() {
           }`))
           }`))
 	So(err, ShouldBeNil)
 	So(err, ShouldBeNil)
 
 
-	condition, err := NewQueryCondition(jsonModel)
+	condition, err := NewQueryCondition(jsonModel, 0)
 	So(err, ShouldBeNil)
 	So(err, ShouldBeNil)
 
 
 	condition.HandleRequest = func(req *tsdb.Request) (*tsdb.Response, error) {
 	condition.HandleRequest = func(req *tsdb.Request) (*tsdb.Response, error) {

+ 1 - 1
pkg/services/alerting/handler.go

@@ -33,7 +33,7 @@ func (e *HandlerImpl) Execute(context *AlertResultContext) {
 		context.EndTime = time.Now()
 		context.EndTime = time.Now()
 		e.log.Debug("Job Execution timeout", "alertId", context.Rule.Id)
 		e.log.Debug("Job Execution timeout", "alertId", context.Rule.Id)
 	case <-context.DoneChan:
 	case <-context.DoneChan:
-		e.log.Debug("Job Execution done", "timing", context.GetDurationSeconds(), "alertId", context.Rule.Id)
+		e.log.Debug("Job Execution done", "timing", context.GetDurationSeconds(), "alertId", context.Rule.Id, "triggered", context.Triggered)
 	}
 	}
 
 
 }
 }

+ 3 - 2
pkg/services/alerting/models.go

@@ -30,7 +30,7 @@ func (aj *AlertJob) IncRetry() {
 type AlertResultContext struct {
 type AlertResultContext struct {
 	Triggered   bool
 	Triggered   bool
 	IsTestRun   bool
 	IsTestRun   bool
-	Details     []*AlertResultDetail
+	Events      []*AlertEvent
 	Logs        []*AlertResultLogEntry
 	Logs        []*AlertResultLogEntry
 	Error       error
 	Error       error
 	Description string
 	Description string
@@ -51,6 +51,7 @@ func NewAlertResultContext(rule *AlertRule) *AlertResultContext {
 		StartTime:  time.Now(),
 		StartTime:  time.Now(),
 		Rule:       rule,
 		Rule:       rule,
 		Logs:       make([]*AlertResultLogEntry, 0),
 		Logs:       make([]*AlertResultLogEntry, 0),
+		Events:     make([]*AlertEvent, 0),
 		DoneChan:   make(chan bool, 1),
 		DoneChan:   make(chan bool, 1),
 		CancelChan: make(chan bool, 1),
 		CancelChan: make(chan bool, 1),
 		log:        log.New("alerting.engine"),
 		log:        log.New("alerting.engine"),
@@ -62,7 +63,7 @@ type AlertResultLogEntry struct {
 	Data    interface{}
 	Data    interface{}
 }
 }
 
 
-type AlertResultDetail struct {
+type AlertEvent struct {
 	Value  float64
 	Value  float64
 	Metric string
 	Metric string
 	State  string
 	State  string

+ 4 - 6
pkg/services/alerting/result_handler.go

@@ -4,7 +4,6 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/bus"
-	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/log"
 	m "github.com/grafana/grafana/pkg/models"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/alerting/alertstates"
 	"github.com/grafana/grafana/pkg/services/alerting/alertstates"
@@ -37,11 +36,10 @@ func (handler *ResultHandlerImpl) Handle(result *AlertResultContext) {
 
 
 	if handler.shouldUpdateState(result, newState) {
 	if handler.shouldUpdateState(result, newState) {
 		cmd := &m.UpdateAlertStateCommand{
 		cmd := &m.UpdateAlertStateCommand{
-			AlertId:         result.Rule.Id,
-			Info:            result.Description,
-			OrgId:           result.Rule.OrgId,
-			State:           newState,
-			TriggeredAlerts: simplejson.NewFromAny(result.Details),
+			AlertId: result.Rule.Id,
+			Info:    result.Description,
+			OrgId:   result.Rule.OrgId,
+			State:   newState,
 		}
 		}
 
 
 		if err := bus.Dispatch(cmd); err != nil {
 		if err := bus.Dispatch(cmd); err != nil {

+ 5 - 6
pkg/services/sqlstore/alert_state.go

@@ -51,12 +51,11 @@ func SetNewAlertState(cmd *m.UpdateAlertStateCommand) error {
 		sess.Id(alert.Id).Update(&alert)
 		sess.Id(alert.Id).Update(&alert)
 
 
 		alertState := m.AlertState{
 		alertState := m.AlertState{
-			AlertId:         cmd.AlertId,
-			OrgId:           cmd.OrgId,
-			State:           cmd.State,
-			Info:            cmd.Info,
-			Created:         time.Now(),
-			TriggeredAlerts: cmd.TriggeredAlerts,
+			AlertId: cmd.AlertId,
+			OrgId:   cmd.OrgId,
+			State:   cmd.State,
+			Info:    cmd.Info,
+			Created: time.Now(),
 		}
 		}
 
 
 		sess.Insert(&alertState)
 		sess.Insert(&alertState)

+ 1 - 1
public/app/features/alerting/alert_log_ctrl.ts

@@ -22,7 +22,7 @@ export class AlertLogCtrl {
   loadAlertLogs(alertId: number) {
   loadAlertLogs(alertId: number) {
     this.backendSrv.get(`/api/alerts/${alertId}/states`).then(result => {
     this.backendSrv.get(`/api/alerts/${alertId}/states`).then(result => {
       this.alertLogs = _.map(result, log => {
       this.alertLogs = _.map(result, log => {
-        log.iconCss = alertDef.getCssForState(log.newState);
+        log.iconCss = alertDef.getCssForState(log.state);
         log.humanTime = moment(log.created).format("YYYY-MM-DD HH:mm:ss");
         log.humanTime = moment(log.created).format("YYYY-MM-DD HH:mm:ss");
         return log;
         return log;
       });
       });

+ 0 - 49
public/app/features/alerting/partials/alert_log.html

@@ -6,55 +6,6 @@
     <h1>Alert history for {{ctrl.alert.title}}</h1>
     <h1>Alert history for {{ctrl.alert.title}}</h1>
   </div>
   </div>
 
 
-  <div class="gf-form-group section" >
-    <h5 class="section-heading">Thresholds</h5>
-    <div class="gf-form">
-      <span class="gf-form-label width-9">
-        <i class="icon-gf icon-gf-warn alert-icon-warn"></i>
-        Warn level
-      </span>
-      <div class="gf-form-label max-width-10">
-        {{ctrl.alert.warnOperator}}
-      </div>
-      <div class="gf-form-label max-width-10">
-        {{ctrl.alert.warnLevel}}
-      </div>
-    </div>
-    <div class="gf-form">
-      <span class="gf-form-label width-9">
-        <i class="icon-gf icon-gf-critical alert-icon-critical"></i>
-        Critical level
-      </span>
-      <div class="gf-form-label max-width-10">
-        {{ctrl.alert.critOperator}}
-      </div>
-      <div class="gf-form-label max-width-10">
-        {{ctrl.alert.critLevel}}
-      </div>
-    </div>
-  </div>
-
-  <div class="gf-form-group section" >
-    <h5 class="section-heading">Aggregators</h5>
-    <div class="gf-form">
-      <span class="gf-form-label width-12">
-        Aggregator
-      </span>
-      <div class="gf-form-label max-width-10">
-        {{ctrl.alert.aggregator}}
-      </div>
-    </div>
-    <div class="gf-form">
-      <span class="gf-form-label width-12">Query range  (seconds)</span>
-      <span class="gf-form-label width-10">{{ctrl.alert.queryRange}}</span>
-    </div>
-
-    <div class="gf-form">
-      <span class="gf-form-label width-12">Frequency (seconds)</span>
-      <span class="gf-form-label width-10">{{ctrl.alert.frequency}}</span>
-    </div>
-  </div>
-
   <table class="filter-table">
   <table class="filter-table">
     <thead>
     <thead>
       <th style="width: 68px">Status</th>
       <th style="width: 68px">Status</th>