1package notifiers 2 3import ( 4 "encoding/json" 5 "strconv" 6 "strings" 7 8 "fmt" 9 10 "github.com/grafana/grafana/pkg/bus" 11 "github.com/grafana/grafana/pkg/infra/log" 12 "github.com/grafana/grafana/pkg/models" 13 "github.com/grafana/grafana/pkg/services/alerting" 14) 15 16func init() { 17 alerting.RegisterNotifier(&alerting.NotifierPlugin{ 18 Type: "hipchat", 19 Name: "HipChat", 20 Description: "Sends notifications uto a HipChat Room", 21 Heading: "HipChat settings", 22 Factory: NewHipChatNotifier, 23 Options: []alerting.NotifierOption{ 24 { 25 Label: "Hip Chat Url", 26 Element: alerting.ElementTypeInput, 27 InputType: alerting.InputTypeText, 28 Placeholder: "HipChat URL (ex https://grafana.hipchat.com)", 29 PropertyName: "url", 30 Required: true, 31 }, 32 { 33 Label: "API Key", 34 Element: alerting.ElementTypeInput, 35 InputType: alerting.InputTypeText, 36 Placeholder: "HipChat API Key", 37 PropertyName: "apiKey", 38 Required: true, 39 }, 40 { 41 Label: "Room ID", 42 Element: alerting.ElementTypeInput, 43 InputType: alerting.InputTypeText, 44 PropertyName: "roomid", 45 }, 46 }, 47 }) 48} 49 50const ( 51 maxFieldCount int = 4 52) 53 54// NewHipChatNotifier is the constructor functions 55// for the HipChatNotifier 56func NewHipChatNotifier(model *models.AlertNotification, _ alerting.GetDecryptedValueFn) (alerting.Notifier, error) { 57 url := model.Settings.Get("url").MustString() 58 if strings.HasSuffix(url, "/") { 59 url = url[:len(url)-1] 60 } 61 if url == "" { 62 return nil, alerting.ValidationError{Reason: "Could not find url property in settings"} 63 } 64 65 apikey := model.Settings.Get("apikey").MustString() 66 roomID := model.Settings.Get("roomid").MustString() 67 68 return &HipChatNotifier{ 69 NotifierBase: NewNotifierBase(model), 70 URL: url, 71 APIKey: apikey, 72 RoomID: roomID, 73 log: log.New("alerting.notifier.hipchat"), 74 }, nil 75} 76 77// HipChatNotifier is responsible for sending 78// alert notifications to Hipchat. 79type HipChatNotifier struct { 80 NotifierBase 81 URL string 82 APIKey string 83 RoomID string 84 log log.Logger 85} 86 87// Notify sends an alert notification to HipChat 88func (hc *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error { 89 hc.log.Info("Executing hipchat notification", "ruleId", evalContext.Rule.ID, "notification", hc.Name) 90 91 ruleURL, err := evalContext.GetRuleURL() 92 if err != nil { 93 hc.log.Error("Failed get rule link", "error", err) 94 return err 95 } 96 97 attributes := make([]map[string]interface{}, 0) 98 for index, evt := range evalContext.EvalMatches { 99 metricName := evt.Metric 100 if len(metricName) > 50 { 101 metricName = metricName[:50] 102 } 103 attributes = append(attributes, map[string]interface{}{ 104 "label": metricName, 105 "value": map[string]interface{}{ 106 "label": strconv.FormatFloat(evt.Value.Float64, 'f', -1, 64), 107 }, 108 }) 109 if index > maxFieldCount { 110 break 111 } 112 } 113 114 if evalContext.Error != nil { 115 attributes = append(attributes, map[string]interface{}{ 116 "label": "Error message", 117 "value": map[string]interface{}{ 118 "label": evalContext.Error.Error(), 119 }, 120 }) 121 } 122 123 message := "" 124 if evalContext.Rule.State != models.AlertStateOK { // don't add message when going back to alert state ok. 125 message += " " + evalContext.Rule.Message 126 } 127 128 if message == "" { 129 message = evalContext.GetNotificationTitle() + " in state " + evalContext.GetStateModel().Text 130 } 131 132 // HipChat has a set list of colors 133 var color string 134 switch evalContext.Rule.State { 135 case models.AlertStateOK: 136 color = "green" 137 case models.AlertStateNoData: 138 color = "gray" 139 case models.AlertStateAlerting: 140 color = "red" 141 default: 142 // Handle other cases? 143 } 144 145 // Add a card with link to the dashboard 146 card := map[string]interface{}{ 147 "style": "application", 148 "url": ruleURL, 149 "id": "1", 150 "title": evalContext.GetNotificationTitle(), 151 "description": message, 152 "icon": map[string]interface{}{ 153 "url": "https://grafana.com/assets/img/fav32.png", 154 }, 155 "date": evalContext.EndTime.Unix(), 156 "attributes": attributes, 157 } 158 if hc.NeedsImage() && evalContext.ImagePublicURL != "" { 159 card["thumbnail"] = map[string]interface{}{ 160 "url": evalContext.ImagePublicURL, 161 "url@2x": evalContext.ImagePublicURL, 162 "width": 1193, 163 "height": 564, 164 } 165 } 166 167 body := map[string]interface{}{ 168 "message": message, 169 "notify": "true", 170 "message_format": "html", 171 "color": color, 172 "card": card, 173 } 174 175 hipURL := fmt.Sprintf("%s/v2/room/%s/notification?auth_token=%s", hc.URL, hc.RoomID, hc.APIKey) 176 data, _ := json.Marshal(&body) 177 hc.log.Info("Request payload", "json", string(data)) 178 cmd := &models.SendWebhookSync{Url: hipURL, Body: string(data)} 179 180 if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil { 181 hc.log.Error("Failed to send hipchat notification", "error", err, "webhook", hc.Name) 182 return err 183 } 184 185 return nil 186} 187