Skip to content

Commit

Permalink
Add console and alert templates with access to all data.
Browse files Browse the repository at this point in the history
Move rulemanager to it's own package to break cicrular dependency.
Make NewTestTieredStorage available to tests, remove duplication.

Change-Id: I33b321245a44aa727bfc3614a7c9ae5005b34e03
  • Loading branch information
brian-brazil committed May 30, 2014
1 parent 16ca35c commit e041c0c
Show file tree
Hide file tree
Showing 16 changed files with 408 additions and 130 deletions.
11 changes: 8 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/notification"
"github.com/prometheus/prometheus/retrieval"
"github.com/prometheus/prometheus/rules"
"github.com/prometheus/prometheus/rules/manager"
"github.com/prometheus/prometheus/storage/metric/tiered"
"github.com/prometheus/prometheus/storage/remote"
"github.com/prometheus/prometheus/storage/remote/opentsdb"
Expand Down Expand Up @@ -82,7 +82,7 @@ type prometheus struct {

unwrittenSamples chan *extraction.Result

ruleManager rules.RuleManager
ruleManager manager.RuleManager
targetManager retrieval.TargetManager
notifications chan notification.NotificationReqs
storage *tiered.TieredStorage
Expand Down Expand Up @@ -272,7 +272,7 @@ func main() {
notifications := make(chan notification.NotificationReqs, *notificationQueueCapacity)

// Queue depth will need to be exposed
ruleManager := rules.NewRuleManager(&rules.RuleManagerOptions{
ruleManager := manager.NewRuleManager(&manager.RuleManagerOptions{
Results: unwrittenSamples,
Notifications: notifications,
EvaluationInterval: conf.EvaluationInterval(),
Expand Down Expand Up @@ -306,6 +306,10 @@ func main() {
RuleManager: ruleManager,
}

consolesHandler := &web.ConsolesHandler{
Storage: ts,
}

databasesHandler := &web.DatabasesHandler{
Provider: ts.DiskStorage,
RefreshInterval: 5 * time.Minute,
Expand Down Expand Up @@ -341,6 +345,7 @@ func main() {
StatusHandler: prometheusStatus,
MetricsHandler: metricsService,
DatabasesHandler: databasesHandler,
ConsolesHandler: consolesHandler,
AlertsHandler: alertsHandler,

QuitDelegate: prometheus.Close,
Expand Down
42 changes: 3 additions & 39 deletions notification/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"io"
"io/ioutil"
"net/http"
"text/template"
"time"

"github.com/golang/glog"
Expand Down Expand Up @@ -84,49 +83,13 @@ func NewNotificationHandler(alertmanagerUrl string, notificationReqs <-chan Noti
}
}

// Interpolate alert information into summary/description templates.
func interpolateMessage(msg string, labels clientmodel.LabelSet, value clientmodel.SampleValue) string {
t := template.New("message")

// Inject some convenience variables that are easier to remember for users
// who are not used to Go's templating system.
defs :=
"{{$labels := .Labels}}" +
"{{$value := .Value}}"

if _, err := t.Parse(defs + msg); err != nil {
glog.Warning("Error parsing template: ", err)
return msg
}

l := map[string]string{}
for k, v := range labels {
l[string(k)] = string(v)
}

tmplData := struct {
Labels map[string]string
Value clientmodel.SampleValue
}{
Labels: l,
Value: value,
}

var buf bytes.Buffer
if err := t.Execute(&buf, &tmplData); err != nil {
glog.Warning("Error executing template: ", err)
return msg
}
return buf.String()
}

// Send a list of notifications to the configured alert manager.
func (n *NotificationHandler) sendNotifications(reqs NotificationReqs) error {
alerts := make([]map[string]interface{}, 0, len(reqs))
for _, req := range reqs {
alerts = append(alerts, map[string]interface{}{
"Summary": interpolateMessage(req.Summary, req.Labels, req.Value),
"Description": interpolateMessage(req.Description, req.Labels, req.Value),
"Summary": req.Summary,
"Description": req.Description,
"Labels": req.Labels,
"Payload": map[string]interface{}{
"Value": req.Value,
Expand All @@ -140,6 +103,7 @@ func (n *NotificationHandler) sendNotifications(reqs NotificationReqs) error {
if err != nil {
return err
}
glog.V(1).Infoln("Sending notifications to alertmanager:", string(buf))
resp, err := n.httpClient.Post(
n.alertmanagerUrl+alertmanagerApiEventsPath,
contentTypeJson,
Expand Down
24 changes: 3 additions & 21 deletions notification/notification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,27 +80,9 @@ func TestNotificationHandler(t *testing.T) {
scenarios := []testNotificationScenario{
{
// Correct message.
summary: "{{$labels.instance}} = {{$value}}",
description: "The alert value for {{$labels.instance}} is {{$value}}",
message: `[{"Description":"The alert value for testinstance is 0.3333333333333333","Labels":{"instance":"testinstance"},"Payload":{"ActiveSince":"0001-01-01T00:00:00Z","AlertingRule":"Test rule string","GeneratorUrl":"prometheus_url","Value":"0.333333"},"Summary":"testinstance = 0.3333333333333333"}]`,
},
{
// Bad message referring to unknown label.
summary: "{{$labels.badlabel}} = {{$value}}",
description: "The alert value for {{$labels.badlabel}} is {{$value}}",
message: `[{"Description":"The alert value for \u003cno value\u003e is 0.3333333333333333","Labels":{"instance":"testinstance"},"Payload":{"ActiveSince":"0001-01-01T00:00:00Z","AlertingRule":"Test rule string","GeneratorUrl":"prometheus_url","Value":"0.333333"},"Summary":"\u003cno value\u003e = 0.3333333333333333"}]`,
},
{
// Bad message referring to unknown variable.
summary: "{{$labels.instance}} = {{$badvar}}",
description: "The alert value for {{$labels.instance}} is {{$badvar}}",
message: `[{"Description":"The alert value for {{$labels.instance}} is {{$badvar}}","Labels":{"instance":"testinstance"},"Payload":{"ActiveSince":"0001-01-01T00:00:00Z","AlertingRule":"Test rule string","GeneratorUrl":"prometheus_url","Value":"0.333333"},"Summary":"{{$labels.instance}} = {{$badvar}}"}]`,
},
{
// Bad message referring to unknown struct field.
summary: "{{$labels.instance}} = {{.Val}}",
description: "The alert value for {{$labels.instance}} is {{.Val}}",
message: `[{"Description":"The alert value for {{$labels.instance}} is {{.Val}}","Labels":{"instance":"testinstance"},"Payload":{"ActiveSince":"0001-01-01T00:00:00Z","AlertingRule":"Test rule string","GeneratorUrl":"prometheus_url","Value":"0.333333"},"Summary":"{{$labels.instance}} = {{.Val}}"}]`,
summary: "Summary",
description: "Description",
message: `[{"Description":"Description","Labels":{"instance":"testinstance"},"Payload":{"ActiveSince":"0001-01-01T00:00:00Z","AlertingRule":"Test rule string","GeneratorUrl":"prometheus_url","Value":"0.333333"},"Summary":"Summary"}]`,
},
}

Expand Down
14 changes: 7 additions & 7 deletions rules/alerting.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ type AlertingRule struct {
// The name of the alert.
name string
// The vector expression from which to generate alerts.
vector ast.VectorNode
Vector ast.VectorNode
// The duration for which a labelset needs to persist in the expression
// output vector before an alert transitions from PENDING to FIRING state.
holdDuration time.Duration
Expand All @@ -119,7 +119,7 @@ func (rule *AlertingRule) Name() string {
}

func (rule *AlertingRule) EvalRaw(timestamp clientmodel.Timestamp, storage metric.PreloadingPersistence) (ast.Vector, error) {
return ast.EvalVectorInstant(rule.vector, timestamp, storage, stats.NewTimerGroup())
return ast.EvalVectorInstant(rule.Vector, timestamp, storage, stats.NewTimerGroup())
}

func (rule *AlertingRule) Eval(timestamp clientmodel.Timestamp, storage metric.PreloadingPersistence) (ast.Vector, error) {
Expand Down Expand Up @@ -185,12 +185,12 @@ func (rule *AlertingRule) ToDotGraph() string {
%#p[shape="box",label="ALERT %s IF FOR %s"];
%#p -> %#p;
%s
}`, &rule, rule.name, utility.DurationToString(rule.holdDuration), &rule, rule.vector, rule.vector.NodeTreeToDotGraph())
}`, &rule, rule.name, utility.DurationToString(rule.holdDuration), &rule, rule.Vector, rule.Vector.NodeTreeToDotGraph())
return graph
}

func (rule *AlertingRule) String() string {
return fmt.Sprintf("ALERT %s IF %s FOR %s WITH %s", rule.name, rule.vector, utility.DurationToString(rule.holdDuration), rule.Labels)
return fmt.Sprintf("ALERT %s IF %s FOR %s WITH %s", rule.name, rule.Vector, utility.DurationToString(rule.holdDuration), rule.Labels)
}

func (rule *AlertingRule) HTMLSnippet() template.HTML {
Expand All @@ -202,8 +202,8 @@ func (rule *AlertingRule) HTMLSnippet() template.HTML {
`ALERT <a href="%s">%s</a> IF <a href="%s">%s</a> FOR %s WITH %s`,
ConsoleLinkForExpression(alertMetric.String()),
rule.name,
ConsoleLinkForExpression(rule.vector.String()),
rule.vector,
ConsoleLinkForExpression(rule.Vector.String()),
rule.Vector,
utility.DurationToString(rule.holdDuration),
rule.Labels))
}
Expand Down Expand Up @@ -236,7 +236,7 @@ func (rule *AlertingRule) ActiveAlerts() []Alert {
func NewAlertingRule(name string, vector ast.VectorNode, holdDuration time.Duration, labels clientmodel.LabelSet, summary string, description string) *AlertingRule {
return &AlertingRule{
name: name,
vector: vector,
Vector: vector,
holdDuration: holdDuration,
Labels: labels,
Summary: summary,
Expand Down
31 changes: 31 additions & 0 deletions rules/ast/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package ast

import (
"encoding/json"
"errors"
"fmt"
"sort"
"strings"
Expand Down Expand Up @@ -200,6 +201,36 @@ func EvalToString(node Node, timestamp clientmodel.Timestamp, format OutputForma
panic("Switch didn't cover all node types")
}

// EvalToVector evaluates the given node into a Vector. Matrices aren't supported.
func EvalToVector(node Node, timestamp clientmodel.Timestamp, storage metric.PreloadingPersistence, queryStats *stats.TimerGroup) (Vector, error) {
viewTimer := queryStats.GetTimer(stats.TotalViewBuildingTime).Start()
viewAdapter, err := viewAdapterForInstantQuery(node, timestamp, storage, queryStats)
viewTimer.Stop()
if err != nil {
panic(err)
}

evalTimer := queryStats.GetTimer(stats.InnerEvalTime).Start()
switch node.Type() {
case SCALAR:
scalar := node.(ScalarNode).Eval(timestamp, viewAdapter)
evalTimer.Stop()
return Vector{&clientmodel.Sample{Value: scalar}}, nil
case VECTOR:
vector := node.(VectorNode).Eval(timestamp, viewAdapter)
evalTimer.Stop()
return vector, nil
case MATRIX:
return nil, errors.New("Matrices not supported by EvalToVector")
case STRING:
str := node.(StringNode).Eval(timestamp, viewAdapter)
evalTimer.Stop()
return Vector{&clientmodel.Sample{
Metric: clientmodel.Metric{"__value__": clientmodel.LabelValue(str)}}}, nil
}
panic("Switch didn't cover all node types")
}

// NodeTreeToDotGraph returns a DOT representation of the scalar
// literal.
func (node *ScalarLiteral) NodeTreeToDotGraph() string {
Expand Down
Loading

0 comments on commit e041c0c

Please sign in to comment.