commit
db402c8c8d
|
@ -18,8 +18,9 @@ following aspects:
|
|||
* sending alert notifications via external services (currently email,
|
||||
[PagerDuty](http://www.pagerduty.com/),
|
||||
[HipChat](http://www.hipchat.com/),
|
||||
[Slack](http://www.slack.com/), or
|
||||
[Pushover](https://www.pushover.net/))
|
||||
[Slack](http://www.slack.com/),
|
||||
[Pushover](https://www.pushover.net/)), or
|
||||
[Flowdock](https://www.flowdock.com/))
|
||||
|
||||
See [config/fixtures/sample.conf.input](config/fixtures/sample.conf.input) for
|
||||
an example config. The full configuration schema including a documentation for
|
||||
|
|
|
@ -77,6 +77,14 @@ func (c Config) Validate() error {
|
|||
return fmt.Errorf("Missing webhook URL in Slack config: %s", proto.MarshalTextString(sc))
|
||||
}
|
||||
}
|
||||
for _, fc := range nc.FlowdockConfig {
|
||||
if fc.ApiToken == nil {
|
||||
return fmt.Errorf("Missing API token in Flowdock config: %s", proto.MarshalTextString(fc))
|
||||
}
|
||||
if fc.FromAddress == nil {
|
||||
return fmt.Errorf("Missing from_address Flowdock config: %s", proto.MarshalTextString(fc))
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := ncNames[nc.GetName()]; ok {
|
||||
return fmt.Errorf("Notification config name not unique: %s", nc.GetName())
|
||||
|
|
|
@ -70,6 +70,17 @@ message SlackConfig {
|
|||
optional bool send_resolved = 5 [default = false];
|
||||
}
|
||||
|
||||
message FlowdockConfig {
|
||||
// Flowdock flow API token.
|
||||
optional string api_token = 1;
|
||||
// Flowdock from_address.
|
||||
optional string from_address = 2;
|
||||
// Flowdock flow tags.
|
||||
repeated string tag = 3;
|
||||
// Notify when resolved.
|
||||
optional bool send_resolved = 4 [default = false];
|
||||
}
|
||||
|
||||
// Notification configuration definition.
|
||||
message NotificationConfig {
|
||||
// Name of this NotificationConfig. Referenced from AggregationRule.
|
||||
|
@ -84,6 +95,8 @@ message NotificationConfig {
|
|||
repeated HipChatConfig hipchat_config = 5;
|
||||
// Zero or more slack notification configurations.
|
||||
repeated SlackConfig slack_config = 6;
|
||||
// Zero or more Flowdock notification configurations.
|
||||
repeated FlowdockConfig flowdock_config = 7;
|
||||
}
|
||||
|
||||
// A regex-based label filter used in aggregations.
|
||||
|
|
|
@ -19,6 +19,11 @@ notification_config {
|
|||
webhook_url: "webhookurl"
|
||||
send_resolved: true
|
||||
}
|
||||
flowdock_config {
|
||||
api_token: "4c7234902348234902384234234cdb59"
|
||||
from_address: "aliaswithgravatar@somehwere.com"
|
||||
tag: "monitoring"
|
||||
}
|
||||
}
|
||||
|
||||
aggregation_rule {
|
||||
|
|
|
@ -14,6 +14,7 @@ It has these top-level messages:
|
|||
PushoverConfig
|
||||
HipChatConfig
|
||||
SlackConfig
|
||||
FlowdockConfig
|
||||
NotificationConfig
|
||||
Filter
|
||||
AggregationRule
|
||||
|
@ -241,6 +242,52 @@ func (m *SlackConfig) GetSendResolved() bool {
|
|||
return Default_SlackConfig_SendResolved
|
||||
}
|
||||
|
||||
type FlowdockConfig struct {
|
||||
// Flowdock flow API token.
|
||||
ApiToken *string `protobuf:"bytes,1,opt,name=api_token" json:"api_token,omitempty"`
|
||||
// Flowdock from_address.
|
||||
FromAddress *string `protobuf:"bytes,2,opt,name=from_address" json:"from_address,omitempty"`
|
||||
// Flowdock flow tags.
|
||||
Tag []string `protobuf:"bytes,3,rep,name=tag" json:"tag,omitempty"`
|
||||
// Notify when resolved.
|
||||
SendResolved *bool `protobuf:"varint,4,opt,name=send_resolved,def=0" json:"send_resolved,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *FlowdockConfig) Reset() { *m = FlowdockConfig{} }
|
||||
func (m *FlowdockConfig) String() string { return proto.CompactTextString(m) }
|
||||
func (*FlowdockConfig) ProtoMessage() {}
|
||||
|
||||
const Default_FlowdockConfig_SendResolved bool = false
|
||||
|
||||
func (m *FlowdockConfig) GetApiToken() string {
|
||||
if m != nil && m.ApiToken != nil {
|
||||
return *m.ApiToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *FlowdockConfig) GetFromAddress() string {
|
||||
if m != nil && m.FromAddress != nil {
|
||||
return *m.FromAddress
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *FlowdockConfig) GetTag() []string {
|
||||
if m != nil {
|
||||
return m.Tag
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *FlowdockConfig) GetSendResolved() bool {
|
||||
if m != nil && m.SendResolved != nil {
|
||||
return *m.SendResolved
|
||||
}
|
||||
return Default_FlowdockConfig_SendResolved
|
||||
}
|
||||
|
||||
// Notification configuration definition.
|
||||
type NotificationConfig struct {
|
||||
// Name of this NotificationConfig. Referenced from AggregationRule.
|
||||
|
@ -254,8 +301,10 @@ type NotificationConfig struct {
|
|||
// Zero or more hipchat notification configurations.
|
||||
HipchatConfig []*HipChatConfig `protobuf:"bytes,5,rep,name=hipchat_config" json:"hipchat_config,omitempty"`
|
||||
// Zero or more slack notification configurations.
|
||||
SlackConfig []*SlackConfig `protobuf:"bytes,6,rep,name=slack_config" json:"slack_config,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
SlackConfig []*SlackConfig `protobuf:"bytes,6,rep,name=slack_config" json:"slack_config,omitempty"`
|
||||
// Zero or more Flowdock notification configurations.
|
||||
FlowdockConfig []*FlowdockConfig `protobuf:"bytes,7,rep,name=flowdock_config" json:"flowdock_config,omitempty"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (m *NotificationConfig) Reset() { *m = NotificationConfig{} }
|
||||
|
@ -304,6 +353,13 @@ func (m *NotificationConfig) GetSlackConfig() []*SlackConfig {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *NotificationConfig) GetFlowdockConfig() []*FlowdockConfig {
|
||||
if m != nil {
|
||||
return m.FlowdockConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A regex-based label filter used in aggregations.
|
||||
type Filter struct {
|
||||
// The regex matching the label name.
|
||||
|
|
|
@ -65,6 +65,7 @@ var (
|
|||
smtpSmartHost = flag.String("notification.smtp.smarthost", "", "Address of the smarthost to send all email notifications to.")
|
||||
smtpSender = flag.String("notification.smtp.sender", "alertmanager@example.org", "Sender email address to use in email notifications.")
|
||||
hipchatURL = flag.String("notification.hipchat.url", "https://api.hipchat.com/v2", "HipChat API V2 URL.")
|
||||
flowdockURL = flag.String("notification.flowdock.url", "https://api.flowdock.com/v1/messages/team_inbox", "Flowdock API V1 URL.")
|
||||
)
|
||||
|
||||
type notificationOp int
|
||||
|
@ -325,6 +326,62 @@ func (n *notifier) sendSlackNotification(op notificationOp, config *pb.SlackConf
|
|||
return nil
|
||||
}
|
||||
|
||||
type flowdockMessage struct {
|
||||
Source string `json:"source"`
|
||||
FromAddress string `json:"from_address"`
|
||||
Subject string `json:"subject"`
|
||||
Content string `json:"content"`
|
||||
Format string `json:"format"`
|
||||
Link string `json:"link"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
func (n *notifier) sendFlowdockNotification(op notificationOp, config *pb.FlowdockConfig, a *Alert) error {
|
||||
flowdockMessage := newFlowdockMessage(op, config, a)
|
||||
url := strings.TrimRight(*flowdockURL, "/") + "/" + config.GetApiToken()
|
||||
jsonMessage, err := json.Marshal(flowdockMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpResponse, err := postJSON(jsonMessage, url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := processResponse(httpResponse, "Flowdock", a); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newFlowdockMessage(op notificationOp, config *pb.FlowdockConfig, a *Alert) *flowdockMessage {
|
||||
status := ""
|
||||
switch op {
|
||||
case notificationOpTrigger:
|
||||
status = "firing"
|
||||
case notificationOpResolve:
|
||||
status = "resolved"
|
||||
}
|
||||
|
||||
msg := &flowdockMessage{
|
||||
Source: "Prometheus",
|
||||
FromAddress: config.GetFromAddress(),
|
||||
Subject: html.EscapeString(a.Summary),
|
||||
Format: "html",
|
||||
Content: fmt.Sprintf("*%s %s*: %s (<%s|view>)", html.EscapeString(a.Labels["alertname"]), status, html.EscapeString(a.Summary), a.Payload["GeneratorURL"]),
|
||||
Link: a.Payload["GeneratorURL"],
|
||||
Tags: append(config.GetTag(), status),
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
func postJSON(jsonMessage []byte, url string) (*http.Response, error) {
|
||||
timeout := time.Duration(5 * time.Second)
|
||||
client := http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
return client.Post(url, contentTypeJSON, bytes.NewBuffer(jsonMessage))
|
||||
}
|
||||
|
||||
func writeEmailBody(w io.Writer, from, to, status string, a *Alert) error {
|
||||
return writeEmailBodyWithTime(w, from, to, status, a, time.Now())
|
||||
}
|
||||
|
@ -452,6 +509,20 @@ func (n *notifier) sendPushoverNotification(token string, op notificationOp, use
|
|||
return err
|
||||
}
|
||||
|
||||
func processResponse(r *http.Response, targetName string, a *Alert) error {
|
||||
spec := fmt.Sprintf("%s notification for alert %v", targetName, a.Fingerprint())
|
||||
if r == nil {
|
||||
return fmt.Errorf("No HTTP response for %s", spec)
|
||||
}
|
||||
defer r.Body.Close()
|
||||
respBuf, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
glog.Infof("Sent %s. Response: HTTP %d: %s", spec, r.StatusCode, respBuf)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *notifier) handleNotification(a *Alert, op notificationOp, config *pb.NotificationConfig) {
|
||||
for _, pdConfig := range config.PagerdutyConfig {
|
||||
if err := n.sendPagerDutyNotification(pdConfig.GetServiceKey(), op, a); err != nil {
|
||||
|
@ -494,6 +565,14 @@ func (n *notifier) handleNotification(a *Alert, op notificationOp, config *pb.No
|
|||
glog.Errorln("Error sending Slack notification:", err)
|
||||
}
|
||||
}
|
||||
for _, fdConfig := range config.FlowdockConfig {
|
||||
if op == notificationOpResolve && !fdConfig.GetSendResolved() {
|
||||
continue
|
||||
}
|
||||
if err := n.sendFlowdockNotification(op, fdConfig, a); err != nil {
|
||||
glog.Errorln("Error sending Flowdock notification:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *notifier) Dispatch() {
|
||||
|
|
Loading…
Reference in New Issue