Merge pull request #84 from prometheus/runbook-amurl

Add runbook and alertmanager URLs to PD+email notifications.
This commit is contained in:
Julius Volz 2015-06-25 18:53:28 +02:00
commit d91ac2ef11
5 changed files with 63 additions and 23 deletions

28
main.go
View File

@ -15,6 +15,8 @@ package main
import (
"flag"
"fmt"
"net"
"os"
"strings"
"time"
@ -31,9 +33,27 @@ var (
configFile = flag.String("config.file", "alertmanager.conf", "Alert Manager configuration file name.")
silencesFile = flag.String("silences.file", "silences.json", "Silence storage file name.")
minRefreshPeriod = flag.Duration("alerts.min-refresh-period", 5*time.Minute, "Minimum required alert refresh period before an alert is purged.")
listenAddress = flag.String("web.listen-address", ":9093", "Address to listen on for the web interface and API.")
pathPrefix = flag.String("web.path-prefix", "/", "Prefix for all web paths.")
hostname = flag.String("web.hostname", "", "Hostname on which the Alertmanager is available to the outside world.")
)
func alertmanagerURL(hostname, pathPrefix, addr string) (string, error) {
var err error
if hostname == "" {
hostname, err = os.Hostname()
if err != nil {
return "", err
}
}
_, port, err := net.SplitHostPort(addr)
if err != nil {
return "", err
}
return fmt.Sprintf("http://%s:%s%s", hostname, port, pathPrefix), nil
}
func main() {
flag.Parse()
@ -65,7 +85,11 @@ func main() {
}()
defer saveSilencesTicker.Stop()
notifier := manager.NewNotifier(conf.NotificationConfig)
amURL, err := alertmanagerURL(*hostname, *pathPrefix, *listenAddress)
if err != nil {
log.Fatalln("Error building Alertmanager URL:", err)
}
notifier := manager.NewNotifier(conf.NotificationConfig, amURL)
defer notifier.Close()
inhibitor := new(manager.Inhibitor)
@ -113,7 +137,7 @@ func main() {
},
StatusHandler: statusHandler,
}
go webService.ServeForever(*pathPrefix)
go webService.ServeForever(*listenAddress, *pathPrefix)
// React to configuration changes.
watcher := config.NewFileWatcher(*configFile)

View File

@ -36,6 +36,8 @@ type Alert struct {
Summary string `json:"summary"`
// Long description of alert.
Description string `json:"description"`
// Runbook link or reference for the alert.
Runbook string `json:"runbook"`
// Label value pairs for purpose of aggregation, matching, and disposition
// dispatching. This must minimally include an "alertname" label.
Labels AlertLabelSet `json:"labels"`

View File

@ -51,6 +51,9 @@ Subject: [{{ .Status }}] {{.Alert.Labels.alertname}}: {{.Alert.Summary}}
{{.Alert.Description}}
Alertmanager: {{.AlertmanagerURL}}
{{if .Alert.Runbook}}Runbook entry: {{.Alert.Runbook}}{{end}}
Grouping labels:
{{range $label, $value := .Alert.Labels}}
{{$label}} = "{{$value}}"{{end}}
@ -95,6 +98,8 @@ type notificationReq struct {
type notifier struct {
// Notifications that are queued to be sent.
pendingNotifications chan *notificationReq
// URL that points back to this Alertmanager instance.
alertmanagerURL string
// Mutex to protect the fields below.
mu sync.Mutex
@ -103,9 +108,10 @@ type notifier struct {
}
// NewNotifier construct a new notifier.
func NewNotifier(configs []*pb.NotificationConfig) *notifier {
func NewNotifier(configs []*pb.NotificationConfig, amURL string) *notifier {
notifier := &notifier{
pendingNotifications: make(chan *notificationReq, *notificationBufferSize),
alertmanagerURL: amURL,
}
notifier.SetNotificationConfigs(configs)
return notifier
@ -156,9 +162,13 @@ func (n *notifier) sendPagerDutyNotification(serviceKey string, op notificationO
"event_type": eventType,
"description": a.Description,
"incident_key": incidentKey,
"client": "Prometheus Alertmanager",
"client_url": n.alertmanagerURL,
"details": map[string]interface{}{
"grouping_labels": a.Labels,
"extra_labels": a.Payload,
"runbook": a.Runbook,
"summary": a.Summary,
},
})
if err != nil {
@ -425,23 +435,25 @@ func postJSON(jsonMessage []byte, url string) (*http.Response, error) {
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())
func writeEmailBody(w io.Writer, from, to, status string, a *Alert, amURL string) error {
return writeEmailBodyWithTime(w, from, to, status, a, time.Now(), amURL)
}
func writeEmailBodyWithTime(w io.Writer, from, to, status string, a *Alert, moment time.Time) error {
func writeEmailBodyWithTime(w io.Writer, from, to, status string, a *Alert, moment time.Time, amURL string) error {
err := bodyTmpl.Execute(w, struct {
From string
To string
Date string
Alert *Alert
Status string
From string
To string
Date string
Alert *Alert
Status string
AlertmanagerURL string
}{
From: from,
To: to,
Date: moment.Format("Mon, 2 Jan 2006 15:04:05 -0700"),
Alert: a,
Status: status,
From: from,
To: to,
Date: moment.Format("Mon, 2 Jan 2006 15:04:05 -0700"),
Alert: a,
Status: status,
AlertmanagerURL: amURL,
})
if err != nil {
return err
@ -529,7 +541,7 @@ func (n *notifier) sendEmailNotification(to string, op notificationOp, a *Alert)
}
defer wc.Close()
return writeEmailBody(wc, *smtpSender, to, status, a)
return writeEmailBody(wc, *smtpSender, to, status, a, n.alertmanagerURL)
}
func (n *notifier) sendPushoverNotification(token string, op notificationOp, userKey string, a *Alert) error {

View File

@ -33,6 +33,7 @@ func TestWriteEmailBody(t *testing.T) {
event := &Alert{
Summary: "Testsummary",
Description: "Test alert description, something went wrong here.",
Runbook: "http://runbook/",
Labels: AlertLabelSet{
"alertname": "TestAlert",
"grouping_label1": "grouping_value1",
@ -46,7 +47,7 @@ func TestWriteEmailBody(t *testing.T) {
buf := &bytes.Buffer{}
location, _ := time.LoadLocation("Europe/Amsterdam")
moment := time.Date(1918, 11, 11, 11, 0, 3, 0, location)
writeEmailBodyWithTime(buf, "from@prometheus.io", "to@prometheus.io", "ALERT", event, moment)
writeEmailBodyWithTime(buf, "from@prometheus.io", "to@prometheus.io", "ALERT", event, moment, "http://alertmanager/")
expected := `From: Prometheus Alertmanager <from@prometheus.io>
To: to@prometheus.io
@ -55,6 +56,9 @@ Subject: [ALERT] TestAlert: Testsummary
Test alert description, something went wrong here.
Alertmanager: http://alertmanager/
Runbook entry: http://runbook/
Grouping labels:
alertname = "TestAlert"

View File

@ -30,7 +30,6 @@ import (
// Commandline flags.
var (
listenAddress = flag.String("web.listen-address", ":9093", "Address to listen on for the web interface and API.")
useLocalAssets = flag.Bool("web.use-local-assets", false, "Serve assets and templates from local files instead of from the binary.")
)
@ -41,8 +40,7 @@ type WebService struct {
StatusHandler *StatusHandler
}
func (w WebService) ServeForever(pathPrefix string) error {
func (w WebService) ServeForever(addr string, pathPrefix string) error {
http.Handle(pathPrefix+"favicon.ico", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "", 404)
}))
@ -75,9 +73,9 @@ func (w WebService) ServeForever(pathPrefix string) error {
}
http.Handle(pathPrefix+"api/", w.AlertManagerService.Handler())
log.Info("listening on ", *listenAddress)
log.Info("listening on ", addr)
return http.ListenAndServe(*listenAddress, nil)
return http.ListenAndServe(addr, nil)
}
func getLocalTemplate(name string, pathPrefix string) (*template.Template, error) {