notifier.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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. for _, notification := range query.Result {
  126. not, err := NewNotificationFromDBModel(notification)
  127. if err == nil {
  128. result = append(result, not)
  129. } else {
  130. log.Error("Failed to read notification model", "error", err)
  131. }
  132. }
  133. return result
  134. }
  135. }
  136. func NewNotificationFromDBModel(model *m.AlertNotification) (*Notification, error) {
  137. notifier, err := createNotifier(model.Type, model.Settings)
  138. if err != nil {
  139. return nil, err
  140. }
  141. return &Notification{
  142. Name: model.Name,
  143. Type: model.Type,
  144. Notifierr: notifier,
  145. SendCritical: model.Settings.Get("sendCrit").MustBool(),
  146. SendWarning: model.Settings.Get("sendWarn").MustBool(),
  147. }, nil
  148. }
  149. var createNotifier = func(notificationType string, settings *simplejson.Json) (NotificationDispatcher, error) {
  150. if notificationType == "email" {
  151. to := settings.Get("to").MustString()
  152. if to == "" {
  153. return nil, fmt.Errorf("Could not find to propertie in settings")
  154. }
  155. return &EmailNotifier{
  156. To: to,
  157. log: log.New("alerting.notification.email"),
  158. }, nil
  159. }
  160. url := settings.Get("url").MustString()
  161. if url == "" {
  162. return nil, fmt.Errorf("Could not find url propertie in settings")
  163. }
  164. return &WebhookNotifier{
  165. Url: url,
  166. User: settings.Get("user").MustString(),
  167. Password: settings.Get("password").MustString(),
  168. log: log.New("alerting.notification.webhook"),
  169. }, nil
  170. }