notifier.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. package alerting
  2. import (
  3. "fmt"
  4. "github.com/grafana/grafana/pkg/bus"
  5. "github.com/grafana/grafana/pkg/components/simplejson"
  6. "github.com/grafana/grafana/pkg/log"
  7. m "github.com/grafana/grafana/pkg/models"
  8. "github.com/grafana/grafana/pkg/services/alerting/alertstates"
  9. "github.com/grafana/grafana/pkg/setting"
  10. )
  11. type NotifierImpl struct {
  12. log log.Logger
  13. getNotifications func(orgId int64, notificationGroups []int64) []*Notification
  14. }
  15. func NewNotifier() *NotifierImpl {
  16. log := log.New("alerting.notifier")
  17. return &NotifierImpl{
  18. log: log,
  19. getNotifications: buildGetNotifiers(log),
  20. }
  21. }
  22. func (n NotifierImpl) ShouldDispath(alertResult *AlertResult, notifier *Notification) bool {
  23. warn := alertResult.State == alertstates.Warn && notifier.SendWarning
  24. crit := alertResult.State == alertstates.Critical && notifier.SendCritical
  25. return (warn || crit) || alertResult.State == alertstates.Ok
  26. }
  27. func (n *NotifierImpl) Notify(alertResult *AlertResult) {
  28. notifiers := n.getNotifications(alertResult.AlertJob.Rule.OrgId, alertResult.AlertJob.Rule.NotificationGroups)
  29. for _, notifier := range notifiers {
  30. if n.ShouldDispath(alertResult, notifier) {
  31. n.log.Info("Sending notification", "state", alertResult.State, "type", notifier.Type)
  32. go notifier.Notifierr.Dispatch(alertResult)
  33. }
  34. }
  35. }
  36. type Notification struct {
  37. Name string
  38. Type string
  39. SendWarning bool
  40. SendCritical bool
  41. Notifierr NotificationDispatcher
  42. }
  43. type EmailNotifier struct {
  44. To string
  45. log log.Logger
  46. }
  47. func (this *EmailNotifier) Dispatch(alertResult *AlertResult) {
  48. this.log.Info("Sending email")
  49. grafanaUrl := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort)
  50. if setting.AppSubUrl != "" {
  51. grafanaUrl += "/" + setting.AppSubUrl
  52. }
  53. cmd := &m.SendEmailCommand{
  54. Data: map[string]interface{}{
  55. "Name": "Name",
  56. "State": alertResult.State,
  57. "Description": alertResult.Description,
  58. "TriggeredAlerts": alertResult.TriggeredAlerts,
  59. "DashboardLink": grafanaUrl + "/dashboard/db/alerting",
  60. "AlertPageUrl": grafanaUrl + "/alerting",
  61. "DashboardImage": grafanaUrl + "/render/dashboard-solo/db/alerting?from=1466169458375&to=1466171258375&panelId=1&width=1000&height=500",
  62. },
  63. To: []string{this.To},
  64. Template: "alert_notification.html",
  65. }
  66. err := bus.Dispatch(cmd)
  67. if err != nil {
  68. this.log.Error("Could not send alert notification as email", "error", err)
  69. }
  70. }
  71. type WebhookNotifier struct {
  72. Url string
  73. User string
  74. Password string
  75. log log.Logger
  76. }
  77. func (this *WebhookNotifier) Dispatch(alertResult *AlertResult) {
  78. this.log.Info("Sending webhook")
  79. bodyJSON := simplejson.New()
  80. bodyJSON.Set("name", alertResult.AlertJob.Rule.Name)
  81. bodyJSON.Set("state", alertResult.State)
  82. bodyJSON.Set("trigged", alertResult.TriggeredAlerts)
  83. body, _ := bodyJSON.MarshalJSON()
  84. cmd := &m.SendWebhook{
  85. Url: this.Url,
  86. User: this.User,
  87. Password: this.Password,
  88. Body: string(body),
  89. }
  90. bus.Dispatch(cmd)
  91. }
  92. type NotificationDispatcher interface {
  93. Dispatch(alertResult *AlertResult)
  94. }
  95. func buildGetNotifiers(log log.Logger) func(orgId int64, notificationGroups []int64) []*Notification {
  96. return func(orgId int64, notificationGroups []int64) []*Notification {
  97. query := &m.GetAlertNotificationQuery{
  98. OrgID: orgId,
  99. Ids: notificationGroups,
  100. IncludeAlwaysExecute: true,
  101. }
  102. err := bus.Dispatch(query)
  103. if err != nil {
  104. log.Error("Failed to read notifications", "error", err)
  105. }
  106. var result []*Notification
  107. log.Info("notifiriring", "count", len(query.Result), "groups", notificationGroups)
  108. for _, notification := range query.Result {
  109. not, err := NewNotificationFromDBModel(notification)
  110. if err == nil {
  111. result = append(result, not)
  112. } else {
  113. log.Error("Failed to read notification model", "error", err)
  114. }
  115. }
  116. return result
  117. }
  118. }
  119. func NewNotificationFromDBModel(model *m.AlertNotification) (*Notification, error) {
  120. notifier, err := createNotifier(model.Type, model.Settings)
  121. if err != nil {
  122. return nil, err
  123. }
  124. return &Notification{
  125. Name: model.Name,
  126. Type: model.Type,
  127. Notifierr: notifier,
  128. SendCritical: !model.Settings.Get("ignoreCrit").MustBool(),
  129. SendWarning: !model.Settings.Get("ignoreWarn").MustBool(),
  130. }, nil
  131. }
  132. var createNotifier = func(notificationType string, settings *simplejson.Json) (NotificationDispatcher, error) {
  133. if notificationType == "email" {
  134. to := settings.Get("to").MustString()
  135. if to == "" {
  136. return nil, fmt.Errorf("Could not find to propertie in settings")
  137. }
  138. return &EmailNotifier{
  139. To: to,
  140. log: log.New("alerting.notification.email"),
  141. }, nil
  142. }
  143. url := settings.Get("url").MustString()
  144. if url == "" {
  145. return nil, fmt.Errorf("Could not find url propertie in settings")
  146. }
  147. return &WebhookNotifier{
  148. Url: url,
  149. User: settings.Get("user").MustString(),
  150. Password: settings.Get("password").MustString(),
  151. log: log.New("alerting.notification.webhook"),
  152. }, nil
  153. }