notifier.go 5.5 KB

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