mirror of
https://github.com/grafana/grafana.git
synced 2025-02-15 10:03:33 -06:00
feat(alerting): more work on notifications
This commit is contained in:
parent
a6c6094775
commit
14eba30f63
@ -1,195 +1,197 @@
|
||||
package alerting
|
||||
|
||||
// type NotifierImpl struct {
|
||||
// log log.Logger
|
||||
// getNotifications func(orgId int64, notificationGroups []int64) []*Notification
|
||||
// }
|
||||
//
|
||||
// func NewNotifier() *NotifierImpl {
|
||||
// log := log.New("alerting.notifier")
|
||||
// return &NotifierImpl{
|
||||
// log: log,
|
||||
// getNotifications: buildGetNotifiers(log),
|
||||
// }
|
||||
// }
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
// func (n NotifierImpl) ShouldDispath(alertResult *AlertResultContext, notifier *Notification) bool {
|
||||
// warn := alertResult.State == alertstates.Warn && notifier.SendWarning
|
||||
// crit := alertResult.State == alertstates.Critical && notifier.SendCritical
|
||||
// return (warn || crit) || alertResult.State == alertstates.Ok
|
||||
// }
|
||||
//
|
||||
// func (n *NotifierImpl) Notify(alertResult *AlertResultContext) {
|
||||
// notifiers := n.getNotifications(alertResult.Rule.OrgId, alertResult.Rule.Notifications)
|
||||
//
|
||||
// for _, notifier := range notifiers {
|
||||
// if n.ShouldDispath(alertResult, notifier) {
|
||||
// n.log.Info("Sending notification", "state", alertResult.State, "type", notifier.Type)
|
||||
// go notifier.Notifierr.Dispatch(alertResult)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// 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},
|
||||
// }
|
||||
//
|
||||
// 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
|
||||
// }
|
||||
//
|
||||
// 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",
|
||||
// }
|
||||
//
|
||||
// err := bus.Dispatch(cmd)
|
||||
// if err != nil {
|
||||
// this.log.Error("Could not send alert notification as email", "error", err)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// type WebhookNotifier struct {
|
||||
// Url string
|
||||
// User string
|
||||
// Password string
|
||||
// log log.Logger
|
||||
// }
|
||||
//
|
||||
// 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)
|
||||
// }
|
||||
//
|
||||
// type NotificationDispatcher interface {
|
||||
// Dispatch(alertResult *AlertResult)
|
||||
// }
|
||||
//
|
||||
// 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)
|
||||
// }
|
||||
//
|
||||
// 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
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func NewNotificationFromDBModel(model *m.AlertNotification) (*Notification, error) {
|
||||
// notifier, err := createNotifier(model.Type, model.Settings)
|
||||
//
|
||||
// if err != nil {
|
||||
// return nil, 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")
|
||||
// }
|
||||
//
|
||||
// return &EmailNotifier{
|
||||
// To: to,
|
||||
// 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
|
||||
// }
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type NotifierImpl struct {
|
||||
log log.Logger
|
||||
getNotifications func(orgId int64, notificationGroups []int64) []*Notification
|
||||
}
|
||||
|
||||
func NewNotifier() *NotifierImpl {
|
||||
log := log.New("alerting.notifier")
|
||||
return &NotifierImpl{
|
||||
log: log,
|
||||
getNotifications: buildGetNotifiers(log),
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifierImpl) Notify(alertResult *AlertResultContext) {
|
||||
notifiers := n.getNotifications(alertResult.Rule.OrgId, alertResult.Rule.Notifications)
|
||||
|
||||
for _, notifier := range notifiers {
|
||||
n.log.Info("Sending notification", "state", alertResult.State, "type", notifier.Type)
|
||||
go notifier.Notifierr.Dispatch(alertResult)
|
||||
}
|
||||
}
|
||||
|
||||
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},
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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",
|
||||
}
|
||||
|
||||
err := bus.Dispatch(cmd)
|
||||
if err != nil {
|
||||
this.log.Error("Could not send alert notification as email", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
type WebhookNotifier struct {
|
||||
Url string
|
||||
User string
|
||||
Password string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
type NotificationDispatcher interface {
|
||||
Dispatch(alertResult *AlertResult)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
func NewNotificationFromDBModel(model *m.AlertNotification) (*Notification, error) {
|
||||
notifier, err := createNotifier(model.Type, model.Settings)
|
||||
|
||||
if err != nil {
|
||||
return nil, 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")
|
||||
}
|
||||
|
||||
return &EmailNotifier{
|
||||
To: to,
|
||||
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
|
||||
}
|
||||
|
@ -1,71 +0,0 @@
|
||||
package transformers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
)
|
||||
|
||||
func NewAggregationTransformer(method string) *AggregationTransformer {
|
||||
return &AggregationTransformer{
|
||||
Method: method,
|
||||
}
|
||||
}
|
||||
|
||||
type AggregationTransformer struct {
|
||||
Method string
|
||||
}
|
||||
|
||||
func (at *AggregationTransformer) Transform(timeserie *tsdb.TimeSeries) (float64, error) {
|
||||
|
||||
if at.Method == "avg" {
|
||||
sum := float64(0)
|
||||
for _, point := range timeserie.Points {
|
||||
sum += point[0]
|
||||
}
|
||||
|
||||
return sum / float64(len(timeserie.Points)), nil
|
||||
}
|
||||
|
||||
if at.Method == "sum" {
|
||||
sum := float64(0)
|
||||
|
||||
for _, v := range timeserie.Points {
|
||||
sum += v[0]
|
||||
}
|
||||
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
if at.Method == "min" {
|
||||
min := timeserie.Points[0][0]
|
||||
|
||||
for _, v := range timeserie.Points {
|
||||
if v[0] < min {
|
||||
min = v[0]
|
||||
}
|
||||
}
|
||||
|
||||
return min, nil
|
||||
}
|
||||
|
||||
if at.Method == "max" {
|
||||
max := timeserie.Points[0][0]
|
||||
|
||||
for _, v := range timeserie.Points {
|
||||
if v[0] > max {
|
||||
max = v[0]
|
||||
}
|
||||
}
|
||||
|
||||
return max, nil
|
||||
}
|
||||
|
||||
if at.Method == "mean" {
|
||||
midPosition := int64(math.Floor(float64(len(timeserie.Points)) / float64(2)))
|
||||
return timeserie.Points[midPosition][0], nil
|
||||
}
|
||||
|
||||
return float64(0), fmt.Errorf("Missing method")
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package transformers
|
||||
|
||||
import "github.com/grafana/grafana/pkg/tsdb"
|
||||
|
||||
type Transformer interface {
|
||||
Transform(timeserie *tsdb.TimeSeries) (float64, error)
|
||||
}
|
Loading…
Reference in New Issue
Block a user