mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 10:53:50 +08:00
feat(alerting): progress on testing alerts
This commit is contained in:
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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"`
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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()">
|
||||
|
Reference in New Issue
Block a user