Browse Source

feat(notifications): refactoring notification handling

Torkel Ödegaard 9 năm trước cách đây
mục cha
commit
8df558dece

+ 1 - 0
pkg/services/alerting/interfaces.go

@@ -17,6 +17,7 @@ type Scheduler interface {
 
 type Notifier interface {
 	Notify(alertResult *AlertResultContext)
+	GetType() string
 }
 
 type AlertCondition interface {

+ 108 - 149
pkg/services/alerting/notifier.go

@@ -1,197 +1,156 @@
 package alerting
 
 import (
+	"errors"
 	"fmt"
-	"log"
-	"strconv"
+	"strings"
 
 	"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/setting"
 )
 
-type NotifierImpl struct {
-	log              log.Logger
-	getNotifications func(orgId int64, notificationGroups []int64) []*Notification
+type RootNotifier struct {
+	NotifierBase
+	log log.Logger
 }
 
-func NewNotifier() *NotifierImpl {
-	log := log.New("alerting.notifier")
-	return &NotifierImpl{
-		log:              log,
-		getNotifications: buildGetNotifiers(log),
+func NewRootNotifier() *RootNotifier {
+	return &RootNotifier{
+		log: log.New("alerting.notifier"),
 	}
 }
 
-func (n *NotifierImpl) Notify(alertResult *AlertResultContext) {
-	notifiers := n.getNotifications(alertResult.Rule.OrgId, alertResult.Rule.Notifications)
+func (n *RootNotifier) Notify(context *AlertResultContext) {
+	notifiers, err := n.getNotifiers(context.Rule.OrgId, context.Rule.Notifications)
+	if err != nil {
+		n.log.Error("Failed to read notifications", "error", err)
+		return
+	}
 
 	for _, notifier := range notifiers {
-		n.log.Info("Sending notification", "state", alertResult.State, "type", notifier.Type)
-		go notifier.Notifierr.Dispatch(alertResult)
+		n.log.Info("Sending notification", "firing", context.Firing, "type", notifier.GetType())
+		go notifier.Notify(context)
 	}
 }
 
-type Notification struct {
-	Name         string
-	Type         string
-	SendWarning  bool
-	SendCritical bool
-
-	Notifierr NotificationDispatcher
-}
-
-type EmailNotifier struct {
-	To  string
-	log log.Logger
-}
-
-func (this *EmailNotifier) Dispatch(alertResult *AlertResult) {
-	this.log.Info("Sending email")
-	grafanaUrl := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort)
-	if setting.AppSubUrl != "" {
-		grafanaUrl += "/" + setting.AppSubUrl
-	}
-
-	query := &m.GetDashboardsQuery{
-		DashboardIds: []int64{alertResult.AlertJob.Rule.DashboardId},
-	}
+func (n *RootNotifier) getNotifiers(orgId int64, notificationIds []int64) ([]Notifier, error) {
+	query := &m.GetAlertNotificationsQuery{OrgId: orgId, Ids: notificationIds}
 
 	if err := bus.Dispatch(query); err != nil {
-		this.log.Error("Failed to load dashboard", "error", err)
-		return
-	}
-
-	if len(query.Result) != 1 {
-		this.log.Error("Can only support one dashboard", "result", len(query.Result))
-		return
+		return nil, err
 	}
 
-	dashboard := query.Result[0]
-
-	panelId := strconv.Itoa(int(alertResult.AlertJob.Rule.PanelId))
-
-	//TODO: get from alertrule and transforms to seconds
-	from := "1466169458375"
-	to := "1466171258375"
-
-	renderUrl := fmt.Sprintf("%s/render/dashboard-solo/db/%s?from=%s&to=%s&panelId=%s&width=1000&height=500", grafanaUrl, dashboard.Slug, from, to, panelId)
-	cmd := &m.SendEmailCommand{
-		Data: map[string]interface{}{
-			"Name":            "Name",
-			"State":           alertResult.State,
-			"Description":     alertResult.Description,
-			"TriggeredAlerts": alertResult.TriggeredAlerts,
-			"DashboardLink":   grafanaUrl + "/dashboard/db/" + dashboard.Slug,
-			"AlertPageUrl":    grafanaUrl + "/alerting",
-			"DashboardImage":  renderUrl,
-		},
-		To:       []string{this.To},
-		Template: "alert_notification.html",
+	var result []Notifier
+	for _, notification := range query.Result {
+		if not, err := NewNotificationFromDBModel(notification); err != nil {
+			return nil, err
+		} else {
+			result = append(result, not)
+		}
 	}
 
-	err := bus.Dispatch(cmd)
-	if err != nil {
-		this.log.Error("Could not send alert notification as email", "error", err)
-	}
+	return result, nil
 }
 
-type WebhookNotifier struct {
-	Url      string
-	User     string
-	Password string
-	log      log.Logger
+type NotifierBase struct {
+	Name string
+	Type string
 }
 
-func (this *WebhookNotifier) Dispatch(alertResult *AlertResultContext) {
-	this.log.Info("Sending webhook")
-
-	bodyJSON := simplejson.New()
-	bodyJSON.Set("name", alertResult.AlertJob.Rule.Name)
-	bodyJSON.Set("state", alertResult.State)
-	bodyJSON.Set("trigged", alertResult.TriggeredAlerts)
-
-	body, _ := bodyJSON.MarshalJSON()
-
-	cmd := &m.SendWebhook{
-		Url:      this.Url,
-		User:     this.User,
-		Password: this.Password,
-		Body:     string(body),
-	}
-
-	bus.Dispatch(cmd)
+func (n *NotifierBase) GetType() string {
+	return n.Type
 }
 
-type NotificationDispatcher interface {
-	Dispatch(alertResult *AlertResult)
+type EmailNotifier struct {
+	NotifierBase
+	Addresses []string
+	log       log.Logger
 }
 
-func buildGetNotifiers(log log.Logger) func(orgId int64, notificationGroups []int64) []*Notification {
-	return func(orgId int64, notificationGroups []int64) []*Notification {
-		query := &m.GetAlertNotificationQuery{
-			OrgID:                orgId,
-			Ids:                  notificationGroups,
-			IncludeAlwaysExecute: true,
-		}
-		err := bus.Dispatch(query)
-		if err != nil {
-			log.Error("Failed to read notifications", "error", err)
-		}
+func (this *EmailNotifier) Notify(context *AlertResultContext) {
+	this.log.Info("Sending alert notification to %v", this.Addresses)
 
-		var result []*Notification
-		for _, notification := range query.Result {
-			not, err := NewNotificationFromDBModel(notification)
-			if err == nil {
-				result = append(result, not)
-			} else {
-				log.Error("Failed to read notification model", "error", err)
-			}
-		}
-
-		return result
+	slugQuery := &m.GetDashboardSlugByIdQuery{Id: context.Rule.DashboardId}
+	if err := bus.Dispatch(slugQuery); err != nil {
+		this.log.Error("Failed to load dashboard", "error", err)
+		return
 	}
-}
+	dashboardSlug := slugQuery.Result
 
-func NewNotificationFromDBModel(model *m.AlertNotification) (*Notification, error) {
-	notifier, err := createNotifier(model.Type, model.Settings)
+	cmd := &m.SendEmailCommand{
+		Data: map[string]interface{}{
+			"RuleName": context.Rule.Name,
+			"Severity": context.Rule.Severity,
+			"RuleLink": setting.ToAbsUrl("dashboard/db/" + dashboardSlug),
+		},
+		To:       this.Addresses,
+		Template: "alert_notification.html",
+	}
 
+	err := bus.Dispatch(cmd)
 	if err != nil {
-		return nil, err
+		this.log.Error("Failed tosend alert notification email", "error", err)
 	}
-
-	return &Notification{
-		Name:         model.Name,
-		Type:         model.Type,
-		Notifierr:    notifier,
-		SendCritical: model.Settings.Get("sendCrit").MustBool(),
-		SendWarning:  model.Settings.Get("sendWarn").MustBool(),
-	}, nil
 }
 
-var createNotifier = func(notificationType string, settings *simplejson.Json) (NotificationDispatcher, error) {
-	if notificationType == "email" {
-		to := settings.Get("to").MustString()
-
-		if to == "" {
-			return nil, fmt.Errorf("Could not find to propertie in settings")
+// type WebhookNotifier struct {
+// 	Url      string
+// 	User     string
+// 	Password string
+// 	log      log.Logger
+// }
+//
+// func (this *WebhookNotifier) Dispatch(context *AlertResultContext) {
+// 	this.log.Info("Sending webhook")
+//
+// 	bodyJSON := simplejson.New()
+// 	bodyJSON.Set("name", context.AlertJob.Rule.Name)
+// 	bodyJSON.Set("state", context.State)
+// 	bodyJSON.Set("trigged", context.TriggeredAlerts)
+//
+// 	body, _ := bodyJSON.MarshalJSON()
+//
+// 	cmd := &m.SendWebhook{
+// 		Url:      this.Url,
+// 		User:     this.User,
+// 		Password: this.Password,
+// 		Body:     string(body),
+// 	}
+//
+// 	bus.Dispatch(cmd)
+// }
+
+func NewNotificationFromDBModel(model *m.AlertNotification) (Notifier, error) {
+	if model.Type == "email" {
+		addressesString := model.Settings.Get("addresses").MustString()
+
+		if addressesString == "" {
+			return nil, fmt.Errorf("Could not find addresses in settings")
 		}
 
 		return &EmailNotifier{
-			To:  to,
-			log: log.New("alerting.notification.email"),
+			NotifierBase: NotifierBase{
+				Name: model.Name,
+				Type: model.Type,
+			},
+			Addresses: strings.Split(addressesString, "\n"),
+			log:       log.New("alerting.notification.email"),
 		}, nil
 	}
 
-	url := settings.Get("url").MustString()
-	if url == "" {
-		return nil, fmt.Errorf("Could not find url propertie in settings")
-	}
-
-	return &WebhookNotifier{
-		Url:      url,
-		User:     settings.Get("user").MustString(),
-		Password: settings.Get("password").MustString(),
-		log:      log.New("alerting.notification.webhook"),
-	}, nil
+	return nil, errors.New("Unsupported notification type")
+
+	// url := settings.Get("url").MustString()
+	// if url == "" {
+	// 	return nil, fmt.Errorf("Could not find url propertie in settings")
+	// }
+	//
+	// return &WebhookNotifier{
+	// 	Url:      url,
+	// 	User:     settings.Get("user").MustString(),
+	// 	Password: settings.Get("password").MustString(),
+	// 	log:      log.New("alerting.notification.webhook"),
+	// }, nil
 }

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

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

+ 1 - 2
public/app/features/alerting/alerts_ctrl.ts

@@ -49,8 +49,7 @@ export class AlertListCtrl {
 
     this.backendSrv.get('/api/alerts', params).then(result => {
       this.alerts = _.map(result, alert => {
-        alert.severityClass = alertDef.getSeverityClass(alert.severity);
-        alert.stateClass = alertDef.getStateClass(alert.state);
+        alert.severityClass = alertDef.getSeverityIconClass(alert.severity);
         return alert;
       });
     });