mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(alerting): lots of progress on notifications, refactored them out to their own package, restored webhook notitication and added slack notification
This commit is contained in:
10
pkg/services/alerting/notifiers/base.go
Normal file
10
pkg/services/alerting/notifiers/base.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package notifiers
|
||||
|
||||
type NotifierBase struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
func (n *NotifierBase) GetType() string {
|
||||
return n.Type
|
||||
}
|
||||
20
pkg/services/alerting/notifiers/common.go
Normal file
20
pkg/services/alerting/notifiers/common.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func getRuleLink(rule *alerting.AlertRule) (string, error) {
|
||||
slugQuery := &m.GetDashboardSlugByIdQuery{Id: rule.DashboardId}
|
||||
if err := bus.Dispatch(slugQuery); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ruleLink := fmt.Sprintf("%sdashboard/db/%s?fullscreen&edit&tab=alert&panelId=%d", setting.AppUrl, slugQuery.Result, rule.PanelId)
|
||||
return ruleLink, nil
|
||||
}
|
||||
62
pkg/services/alerting/notifiers/email.go
Normal file
62
pkg/services/alerting/notifiers/email.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
)
|
||||
|
||||
func init() {
|
||||
alerting.RegisterNotifier("email", NewEmailNotifier)
|
||||
}
|
||||
|
||||
type EmailNotifier struct {
|
||||
NotifierBase
|
||||
Addresses []string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func NewEmailNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
|
||||
addressesString := model.Settings.Get("addresses").MustString()
|
||||
|
||||
if addressesString == "" {
|
||||
return nil, alerting.AlertValidationError{Reason: "Could not find addresses in settings"}
|
||||
}
|
||||
|
||||
return &EmailNotifier{
|
||||
NotifierBase: NotifierBase{
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
},
|
||||
Addresses: strings.Split(addressesString, "\n"),
|
||||
log: log.New("alerting.notifier.email"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (this *EmailNotifier) Notify(context *alerting.AlertResultContext) {
|
||||
this.log.Info("Sending alert notification to", "addresses", this.Addresses)
|
||||
|
||||
ruleLink, err := getRuleLink(context.Rule)
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
cmd := &m.SendEmailCommand{
|
||||
Data: map[string]interface{}{
|
||||
"RuleState": context.Rule.State,
|
||||
"RuleName": context.Rule.Name,
|
||||
"Severity": context.Rule.Severity,
|
||||
"RuleLink": ruleLink,
|
||||
},
|
||||
To: this.Addresses,
|
||||
Template: "alert_notification.html",
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(cmd); err != nil {
|
||||
this.log.Error("Failed to send alert notification email", "error", err)
|
||||
}
|
||||
}
|
||||
52
pkg/services/alerting/notifiers/email_test.go
Normal file
52
pkg/services/alerting/notifiers/email_test.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestEmailNotifier(t *testing.T) {
|
||||
Convey("Email notifier tests", t, func() {
|
||||
|
||||
Convey("Parsing alert notification from settings", func() {
|
||||
Convey("empty settings should return error", func() {
|
||||
json := `{ }`
|
||||
|
||||
settingsJSON, _ := simplejson.NewJson([]byte(json))
|
||||
model := &m.AlertNotification{
|
||||
Name: "ops",
|
||||
Type: "email",
|
||||
Settings: settingsJSON,
|
||||
}
|
||||
|
||||
_, err := NewEmailNotifier(model)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("from settings", func() {
|
||||
json := `
|
||||
{
|
||||
"addresses": "ops@grafana.org"
|
||||
}`
|
||||
|
||||
settingsJSON, _ := simplejson.NewJson([]byte(json))
|
||||
model := &m.AlertNotification{
|
||||
Name: "ops",
|
||||
Type: "email",
|
||||
Settings: settingsJSON,
|
||||
}
|
||||
|
||||
not, err := NewEmailNotifier(model)
|
||||
emailNotifier := not.(*EmailNotifier)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(emailNotifier.Name, ShouldEqual, "ops")
|
||||
So(emailNotifier.Type, ShouldEqual, "email")
|
||||
So(emailNotifier.Addresses[0], ShouldEqual, "ops@grafana.org")
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
66
pkg/services/alerting/notifiers/slack.go
Normal file
66
pkg/services/alerting/notifiers/slack.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
)
|
||||
|
||||
func init() {
|
||||
alerting.RegisterNotifier("slack", NewSlackNotifier)
|
||||
}
|
||||
|
||||
func NewSlackNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
|
||||
url := model.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
return nil, alerting.AlertValidationError{Reason: "Could not find url property in settings"}
|
||||
}
|
||||
|
||||
return &SlackNotifier{
|
||||
NotifierBase: NotifierBase{
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
},
|
||||
Url: url,
|
||||
log: log.New("alerting.notifier.slack"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type SlackNotifier struct {
|
||||
NotifierBase
|
||||
Url string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (this *SlackNotifier) Notify(context *alerting.AlertResultContext) {
|
||||
this.log.Info("Executing slack notification", "ruleId", context.Rule.Id, "notification", this.Name)
|
||||
|
||||
rule := context.Rule
|
||||
|
||||
ruleLink, err := getRuleLink(rule)
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
stateText := string(rule.Severity)
|
||||
if !context.Firing {
|
||||
stateText = "ok"
|
||||
}
|
||||
|
||||
text := fmt.Sprintf("[%s]: <%s|%s>", stateText, ruleLink, rule.Name)
|
||||
|
||||
body := simplejson.New()
|
||||
body.Set("text", text)
|
||||
|
||||
data, _ := body.MarshalJSON()
|
||||
cmd := &m.SendWebhook{Url: this.Url, Body: string(data)}
|
||||
|
||||
if err := bus.Dispatch(cmd); err != nil {
|
||||
this.log.Error("Failed to send slack notification", "error", err, "webhook", this.Name)
|
||||
}
|
||||
}
|
||||
61
pkg/services/alerting/notifiers/webhook.go
Normal file
61
pkg/services/alerting/notifiers/webhook.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
)
|
||||
|
||||
func init() {
|
||||
alerting.RegisterNotifier("webhook", NewWebHookNotifier)
|
||||
}
|
||||
|
||||
func NewWebHookNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
|
||||
url := model.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
return nil, alerting.AlertValidationError{Reason: "Could not find url property in settings"}
|
||||
}
|
||||
|
||||
return &WebhookNotifier{
|
||||
NotifierBase: NotifierBase{
|
||||
Name: model.Name,
|
||||
Type: model.Type,
|
||||
},
|
||||
Url: url,
|
||||
User: model.Settings.Get("user").MustString(),
|
||||
Password: model.Settings.Get("password").MustString(),
|
||||
log: log.New("alerting.notifier.webhook"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type WebhookNotifier struct {
|
||||
NotifierBase
|
||||
Url string
|
||||
User string
|
||||
Password string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (this *WebhookNotifier) Notify(context *alerting.AlertResultContext) {
|
||||
this.log.Info("Sending webhook")
|
||||
|
||||
bodyJSON := simplejson.New()
|
||||
bodyJSON.Set("name", context.Rule.Name)
|
||||
bodyJSON.Set("firing", context.Firing)
|
||||
bodyJSON.Set("severity", context.Rule.Severity)
|
||||
|
||||
body, _ := bodyJSON.MarshalJSON()
|
||||
|
||||
cmd := &m.SendWebhook{
|
||||
Url: this.Url,
|
||||
User: this.User,
|
||||
Password: this.Password,
|
||||
Body: string(body),
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(cmd); err != nil {
|
||||
this.log.Error("Failed to send webhook", "error", err, "webhook", this.Name)
|
||||
}
|
||||
}
|
||||
52
pkg/services/alerting/notifiers/webhook_test.go
Normal file
52
pkg/services/alerting/notifiers/webhook_test.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestWebhookNotifier(t *testing.T) {
|
||||
Convey("Webhook notifier tests", t, func() {
|
||||
|
||||
Convey("Parsing alert notification from settings", func() {
|
||||
Convey("empty settings should return error", func() {
|
||||
json := `{ }`
|
||||
|
||||
settingsJSON, _ := simplejson.NewJson([]byte(json))
|
||||
model := &m.AlertNotification{
|
||||
Name: "ops",
|
||||
Type: "email",
|
||||
Settings: settingsJSON,
|
||||
}
|
||||
|
||||
_, err := NewWebHookNotifier(model)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("from settings", func() {
|
||||
json := `
|
||||
{
|
||||
"url": "http://google.com"
|
||||
}`
|
||||
|
||||
settingsJSON, _ := simplejson.NewJson([]byte(json))
|
||||
model := &m.AlertNotification{
|
||||
Name: "ops",
|
||||
Type: "email",
|
||||
Settings: settingsJSON,
|
||||
}
|
||||
|
||||
not, err := NewWebHookNotifier(model)
|
||||
emailNotifier := not.(*WebhookNotifier)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(emailNotifier.Name, ShouldEqual, "ops")
|
||||
So(emailNotifier.Type, ShouldEqual, "email")
|
||||
So(emailNotifier.Url, ShouldEqual, "http://google.com")
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user