From f7215608e9fd043821fa21064ac4ca2b28be0dec Mon Sep 17 00:00:00 2001 From: Dave Scott Date: Sun, 21 May 2017 02:02:48 -0600 Subject: [PATCH] Update VictorOps notifier w/ auto resolve and image URL (#8431) * Fixed newline JSON buy in VictorOps integration * Fixed payload sent to VictorOps so that link displays correctly * Fixed formatted to resolve go error * Add auto resolve incidents and image URL to VictorOps notifier --- pkg/services/alerting/notifiers/victorops.go | 51 +++++++++++++++----- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/pkg/services/alerting/notifiers/victorops.go b/pkg/services/alerting/notifiers/victorops.go index a4e34a40b8a..2fbb2f64d1b 100644 --- a/pkg/services/alerting/notifiers/victorops.go +++ b/pkg/services/alerting/notifiers/victorops.go @@ -1,10 +1,10 @@ package notifiers import ( - "encoding/json" "time" "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/models" @@ -15,6 +15,8 @@ import ( // AlertStateCritical - Victorops uses "CRITICAL" string to indicate "Alerting" state const AlertStateCritical = "CRITICAL" +const AlertStateRecovery = "RECOVERY" + func init() { alerting.RegisterNotifier(&alerting.NotifierPlugin{ Type: "victorops", @@ -27,6 +29,15 @@ func init() { Url +
+ + +
`, }) } @@ -34,6 +45,7 @@ func init() { // NewVictoropsNotifier creates an instance of VictoropsNotifier that // handles posting notifications to Victorops REST API func NewVictoropsNotifier(model *models.AlertNotification) (alerting.Notifier, error) { + autoResolve := model.Settings.Get("autoResolve").MustBool(true) url := model.Settings.Get("url").MustString() if url == "" { return nil, alerting.ValidationError{Reason: "Could not find victorops url property in settings"} @@ -42,6 +54,7 @@ func NewVictoropsNotifier(model *models.AlertNotification) (alerting.Notifier, e return &VictoropsNotifier{ NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings), URL: url, + AutoResolve: autoResolve, log: log.New("alerting.notifier.victorops"), }, nil } @@ -51,8 +64,9 @@ func NewVictoropsNotifier(model *models.AlertNotification) (alerting.Notifier, e // Victorops specifications (http://victorops.force.com/knowledgebase/articles/Integration/Alert-Ingestion-API-Documentation/) type VictoropsNotifier struct { NotifierBase - URL string - log log.Logger + URL string + AutoResolve bool + log log.Logger } // Notify sends notification to Victorops via POST to URL endpoint @@ -66,6 +80,11 @@ func (this *VictoropsNotifier) Notify(evalContext *alerting.EvalContext) error { return err } + if evalContext.Rule.State == models.AlertStateOK && !this.AutoResolve { + this.log.Info("Not alerting VictorOps", "state", evalContext.Rule.State, "auto resolve", this.AutoResolve) + return nil + } + fields := make([]map[string]interface{}, 0) fieldLimitCount := 4 for index, evt := range evalContext.EvalMatches { @@ -92,20 +111,28 @@ func (this *VictoropsNotifier) Notify(evalContext *alerting.EvalContext) error { messageType = AlertStateCritical } - body := map[string]interface{}{ - "message_type": messageType, - "entity_id": evalContext.Rule.Name, - "timestamp": time.Now().Unix(), - "state_start_time": evalContext.StartTime.Unix(), - "state_message": evalContext.Rule.Message + "\n" + ruleUrl, - "monitoring_tool": "Grafana v" + setting.BuildVersion, + if evalContext.Rule.State == models.AlertStateOK { + messageType = AlertStateRecovery } - data, _ := json.Marshal(&body) + bodyJSON := simplejson.New() + bodyJSON.Set("message_type", messageType) + bodyJSON.Set("entity_id", evalContext.Rule.Name) + bodyJSON.Set("timestamp", time.Now().Unix()) + bodyJSON.Set("state_start_time", evalContext.StartTime.Unix()) + bodyJSON.Set("state_message", evalContext.Rule.Message) + bodyJSON.Set("monitoring_tool", "Grafana v"+setting.BuildVersion) + bodyJSON.Set("alert_url", ruleUrl) + + if evalContext.ImagePublicUrl != "" { + bodyJSON.Set("image_url", evalContext.ImagePublicUrl) + } + + data, _ := bodyJSON.MarshalJSON() cmd := &models.SendWebhookSync{Url: this.URL, Body: string(data)} if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil { - this.log.Error("Failed to send victorops notification", "error", err, "webhook", this.Name) + this.log.Error("Failed to send Victorops notification", "error", err, "webhook", this.Name) return err }