Add alert-expression console links to notifications.
The ConsoleLinkForExpression() function now escapes console URLs in such a way that works both in emails and in HTML. Change-Id: I917bae0b526cbbac28ccd2a4ec3c5ac03ee4c647
This commit is contained in:
parent
d74c2c54d4
commit
1eb1ceac8c
11
main.go
11
main.go
|
@ -203,14 +203,19 @@ func main() {
|
||||||
notifications := make(chan notification.NotificationReqs, *notificationQueueCapacity)
|
notifications := make(chan notification.NotificationReqs, *notificationQueueCapacity)
|
||||||
|
|
||||||
// Queue depth will need to be exposed
|
// Queue depth will need to be exposed
|
||||||
ruleManager := rules.NewRuleManager(unwrittenSamples, notifications, conf.EvaluationInterval(), ts)
|
ruleManager := rules.NewRuleManager(&rules.RuleManagerOptions{
|
||||||
|
Results: unwrittenSamples,
|
||||||
|
Notifications: notifications,
|
||||||
|
EvaluationInterval: conf.EvaluationInterval(),
|
||||||
|
Storage: ts,
|
||||||
|
PrometheusUrl: web.MustBuildServerUrl(),
|
||||||
|
})
|
||||||
if err := ruleManager.AddRulesFromConfig(conf); err != nil {
|
if err := ruleManager.AddRulesFromConfig(conf); err != nil {
|
||||||
glog.Fatal("Error loading rule files: ", err)
|
glog.Fatal("Error loading rule files: ", err)
|
||||||
}
|
}
|
||||||
go ruleManager.Run()
|
go ruleManager.Run()
|
||||||
|
|
||||||
prometheusUrl := web.MustBuildServerUrl()
|
notificationHandler := notification.NewNotificationHandler(*alertmanagerUrl, notifications)
|
||||||
notificationHandler := notification.NewNotificationHandler(*alertmanagerUrl, prometheusUrl, notifications)
|
|
||||||
go notificationHandler.Run()
|
go notificationHandler.Run()
|
||||||
|
|
||||||
flags := map[string]string{}
|
flags := map[string]string{}
|
||||||
|
|
|
@ -54,6 +54,8 @@ type NotificationReq struct {
|
||||||
ActiveSince time.Time
|
ActiveSince time.Time
|
||||||
// A textual representation of the rule that triggered the alert.
|
// A textual representation of the rule that triggered the alert.
|
||||||
RuleString string
|
RuleString string
|
||||||
|
// Prometheus console link to alert expression.
|
||||||
|
GeneratorUrl string
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotificationReqs []*NotificationReq
|
type NotificationReqs []*NotificationReq
|
||||||
|
@ -67,8 +69,6 @@ type httpPoster interface {
|
||||||
type NotificationHandler struct {
|
type NotificationHandler struct {
|
||||||
// The URL of the alert manager to send notifications to.
|
// The URL of the alert manager to send notifications to.
|
||||||
alertmanagerUrl string
|
alertmanagerUrl string
|
||||||
// The URL of this Prometheus instance to include in notifications.
|
|
||||||
prometheusUrl string
|
|
||||||
// Buffer of notifications that have not yet been sent.
|
// Buffer of notifications that have not yet been sent.
|
||||||
pendingNotifications <-chan NotificationReqs
|
pendingNotifications <-chan NotificationReqs
|
||||||
// HTTP client with custom timeout settings.
|
// HTTP client with custom timeout settings.
|
||||||
|
@ -76,12 +76,11 @@ type NotificationHandler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct a new NotificationHandler.
|
// Construct a new NotificationHandler.
|
||||||
func NewNotificationHandler(alertmanagerUrl string, prometheusUrl string, notificationReqs <-chan NotificationReqs) *NotificationHandler {
|
func NewNotificationHandler(alertmanagerUrl string, notificationReqs <-chan NotificationReqs) *NotificationHandler {
|
||||||
return &NotificationHandler{
|
return &NotificationHandler{
|
||||||
alertmanagerUrl: alertmanagerUrl,
|
alertmanagerUrl: alertmanagerUrl,
|
||||||
pendingNotifications: notificationReqs,
|
pendingNotifications: notificationReqs,
|
||||||
httpClient: utility.NewDeadlineClient(*deadline),
|
httpClient: utility.NewDeadlineClient(*deadline),
|
||||||
prometheusUrl: prometheusUrl,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +131,7 @@ func (n *NotificationHandler) sendNotifications(reqs NotificationReqs) error {
|
||||||
"Payload": map[string]interface{}{
|
"Payload": map[string]interface{}{
|
||||||
"Value": req.Value,
|
"Value": req.Value,
|
||||||
"ActiveSince": req.ActiveSince,
|
"ActiveSince": req.ActiveSince,
|
||||||
"GeneratorUrl": n.prometheusUrl,
|
"GeneratorUrl": req.GeneratorUrl,
|
||||||
"AlertingRule": req.RuleString,
|
"AlertingRule": req.RuleString,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -48,7 +48,7 @@ type testNotificationScenario struct {
|
||||||
func (s *testNotificationScenario) test(i int, t *testing.T) {
|
func (s *testNotificationScenario) test(i int, t *testing.T) {
|
||||||
notifications := make(chan NotificationReqs)
|
notifications := make(chan NotificationReqs)
|
||||||
defer close(notifications)
|
defer close(notifications)
|
||||||
h := NewNotificationHandler("alertmanager_url", "prometheus_url", notifications)
|
h := NewNotificationHandler("alertmanager_url", notifications)
|
||||||
|
|
||||||
receivedPost := make(chan bool, 1)
|
receivedPost := make(chan bool, 1)
|
||||||
poster := testHttpPoster{receivedPost: receivedPost}
|
poster := testHttpPoster{receivedPost: receivedPost}
|
||||||
|
@ -63,9 +63,10 @@ func (s *testNotificationScenario) test(i int, t *testing.T) {
|
||||||
Labels: clientmodel.LabelSet{
|
Labels: clientmodel.LabelSet{
|
||||||
clientmodel.LabelName("instance"): clientmodel.LabelValue("testinstance"),
|
clientmodel.LabelName("instance"): clientmodel.LabelValue("testinstance"),
|
||||||
},
|
},
|
||||||
Value: clientmodel.SampleValue(1.0 / 3.0),
|
Value: clientmodel.SampleValue(1.0 / 3.0),
|
||||||
ActiveSince: time.Time{},
|
ActiveSince: time.Time{},
|
||||||
RuleString: "Test rule string",
|
RuleString: "Test rule string",
|
||||||
|
GeneratorUrl: "prometheus_url",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,8 @@ package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
clientmodel "github.com/prometheus/client_golang/model"
|
clientmodel "github.com/prometheus/client_golang/model"
|
||||||
|
|
||||||
|
@ -116,5 +117,13 @@ func NewMatrix(vector ast.Node, intervalStr string) (ast.MatrixNode, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConsoleLinkForExpression(expr string) string {
|
func ConsoleLinkForExpression(expr string) string {
|
||||||
return html.EscapeString(fmt.Sprintf(`graph#[{"expr":%q,"tab":1}]`, expr))
|
// url.QueryEscape percent-escapes everything except spaces, for which it
|
||||||
|
// uses "+". However, in the non-query part of a URI, only percent-escaped
|
||||||
|
// spaces are legal, so we need to manually replace "+" with "%20" after
|
||||||
|
// query-escaping the string.
|
||||||
|
//
|
||||||
|
// See also:
|
||||||
|
// http://stackoverflow.com/questions/1634271/url-encoding-the-space-character-or-20.
|
||||||
|
urlData := url.QueryEscape(fmt.Sprintf(`[{"expr":%q,"tab":1}]`, expr))
|
||||||
|
return fmt.Sprintf("/graph#%s", strings.Replace(urlData, "+", "%20", -1))
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,21 +45,37 @@ type ruleManager struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
rules []Rule
|
rules []Rule
|
||||||
|
|
||||||
|
done chan bool
|
||||||
|
|
||||||
|
interval time.Duration
|
||||||
|
storage *metric.TieredStorage
|
||||||
|
|
||||||
results chan<- *extraction.Result
|
results chan<- *extraction.Result
|
||||||
notifications chan<- notification.NotificationReqs
|
notifications chan<- notification.NotificationReqs
|
||||||
done chan bool
|
|
||||||
interval time.Duration
|
prometheusUrl string
|
||||||
storage *metric.TieredStorage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRuleManager(results chan<- *extraction.Result, notifications chan<- notification.NotificationReqs, interval time.Duration, storage *metric.TieredStorage) RuleManager {
|
type RuleManagerOptions struct {
|
||||||
|
EvaluationInterval time.Duration
|
||||||
|
Storage *metric.TieredStorage
|
||||||
|
|
||||||
|
Notifications chan<- notification.NotificationReqs
|
||||||
|
Results chan<- *extraction.Result
|
||||||
|
|
||||||
|
PrometheusUrl string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRuleManager(o *RuleManagerOptions) RuleManager {
|
||||||
manager := &ruleManager{
|
manager := &ruleManager{
|
||||||
results: results,
|
rules: []Rule{},
|
||||||
notifications: notifications,
|
done: make(chan bool),
|
||||||
rules: []Rule{},
|
|
||||||
done: make(chan bool),
|
interval: o.EvaluationInterval,
|
||||||
interval: interval,
|
storage: o.Storage,
|
||||||
storage: storage,
|
results: o.Results,
|
||||||
|
notifications: o.Notifications,
|
||||||
|
prometheusUrl: o.PrometheusUrl,
|
||||||
}
|
}
|
||||||
return manager
|
return manager
|
||||||
}
|
}
|
||||||
|
@ -107,9 +123,10 @@ func (m *ruleManager) queueAlertNotifications(rule *AlertingRule) {
|
||||||
Labels: aa.Labels.Merge(clientmodel.LabelSet{
|
Labels: aa.Labels.Merge(clientmodel.LabelSet{
|
||||||
AlertNameLabel: clientmodel.LabelValue(rule.Name()),
|
AlertNameLabel: clientmodel.LabelValue(rule.Name()),
|
||||||
}),
|
}),
|
||||||
Value: aa.Value,
|
Value: aa.Value,
|
||||||
ActiveSince: aa.ActiveSince,
|
ActiveSince: aa.ActiveSince,
|
||||||
RuleString: rule.String(),
|
RuleString: rule.String(),
|
||||||
|
GeneratorUrl: m.prometheusUrl + ConsoleLinkForExpression(rule.vector.String()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
m.notifications <- notifications
|
m.notifications <- notifications
|
||||||
|
|
Loading…
Reference in New Issue