Merge pull request #293 from prometheus/web/show-rules

Improve rule/expression printing and show rules in Status dashboard
This commit is contained in:
juliusv 2013-06-11 02:54:11 -07:00
commit a88a71c6db
7 changed files with 174 additions and 145 deletions

18
main.go
View File

@ -212,6 +212,14 @@ func main() {
targetManager := retrieval.NewTargetManager(scrapeResults, *concurrentRetrievalAllowance) targetManager := retrieval.NewTargetManager(scrapeResults, *concurrentRetrievalAllowance)
targetManager.AddTargetsFromConfig(conf) targetManager.AddTargetsFromConfig(conf)
// Queue depth will need to be exposed
ruleManager := rules.NewRuleManager(ruleResults, conf.EvaluationInterval(), ts)
err = ruleManager.AddRulesFromConfig(conf)
if err != nil {
log.Fatalf("Error loading rule files: %v", err)
}
go ruleManager.Run()
flags := map[string]string{} flags := map[string]string{}
flag.VisitAll(func(f *flag.Flag) { flag.VisitAll(func(f *flag.Flag) {
@ -222,6 +230,7 @@ func main() {
PrometheusStatus: &web.PrometheusStatus{ PrometheusStatus: &web.PrometheusStatus{
BuildInfo: BuildInfo, BuildInfo: BuildInfo,
Config: conf.String(), Config: conf.String(),
Rules: ruleManager.Rules(),
TargetPools: targetManager.Pools(), TargetPools: targetManager.Pools(),
Flags: flags, Flags: flags,
Birth: time.Now(), Birth: time.Now(),
@ -321,15 +330,6 @@ func main() {
} }
}() }()
// Queue depth will need to be exposed
ruleManager := rules.NewRuleManager(ruleResults, conf.EvaluationInterval(), ts)
err = ruleManager.AddRulesFromConfig(conf)
if err != nil {
log.Fatalf("Error loading rule files: %v", err)
}
go ruleManager.Run()
go func() { go func() {
err := webService.ServeForever() err := webService.ServeForever()
if err != nil { if err != nil {

View File

@ -53,12 +53,37 @@ func (l LabelSet) Merge(other LabelSet) LabelSet {
func (l LabelSet) String() string { func (l LabelSet) String() string {
labelStrings := make([]string, 0, len(l)) labelStrings := make([]string, 0, len(l))
for label, value := range l { for label, value := range l {
labelStrings = append(labelStrings, fmt.Sprintf("%s='%s'", label, value)) labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))
} }
switch len(labelStrings) {
case 0:
return ""
default:
sort.Strings(labelStrings) sort.Strings(labelStrings)
return fmt.Sprintf("{%s}", strings.Join(labelStrings, ", ")) return fmt.Sprintf("{%s}", strings.Join(labelStrings, ", "))
}
}
func (m Metric) String() string {
metricName, ok := m[MetricNameLabel]
if !ok {
panic("Tried to print metric without name")
}
labelStrings := make([]string, 0, len(m)-1)
for label, value := range m {
if label != MetricNameLabel {
labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))
}
}
switch len(labelStrings) {
case 0:
return string(metricName)
default:
sort.Strings(labelStrings)
return fmt.Sprintf("%s{%s}", metricName, strings.Join(labelStrings, ", "))
}
} }
func (l LabelSet) ToMetric() Metric { func (l LabelSet) ToMetric() Metric {

View File

@ -75,22 +75,9 @@ func (exprType ExprType) String() string {
func (vector Vector) String() string { func (vector Vector) String() string {
metricStrings := make([]string, 0, len(vector)) metricStrings := make([]string, 0, len(vector))
for _, sample := range vector { for _, sample := range vector {
metricName, ok := sample.Metric[model.MetricNameLabel]
if !ok {
panic("Tried to print vector without metric name")
}
labelStrings := make([]string, 0, len(sample.Metric)-1)
for label, value := range sample.Metric {
if label != model.MetricNameLabel {
// TODO escape special chars in label values here and elsewhere.
labelStrings = append(labelStrings, fmt.Sprintf("%s='%s'", label, value))
}
}
sort.Strings(labelStrings)
metricStrings = append(metricStrings, metricStrings = append(metricStrings,
fmt.Sprintf("%s{%s} => %v @[%v]", fmt.Sprintf("%s => %v @[%v]",
metricName, sample.Metric,
strings.Join(labelStrings, ","),
sample.Value, sample.Timestamp)) sample.Value, sample.Timestamp))
} }
return strings.Join(metricStrings, "\n") return strings.Join(metricStrings, "\n")
@ -106,7 +93,7 @@ func (matrix Matrix) String() string {
labelStrings := make([]string, 0, len(sampleSet.Metric)-1) labelStrings := make([]string, 0, len(sampleSet.Metric)-1)
for label, value := range sampleSet.Metric { for label, value := range sampleSet.Metric {
if label != model.MetricNameLabel { if label != model.MetricNameLabel {
labelStrings = append(labelStrings, fmt.Sprintf("%s='%s'", label, value)) labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))
} }
} }
sort.Strings(labelStrings) sort.Strings(labelStrings)
@ -118,7 +105,7 @@ func (matrix Matrix) String() string {
metricStrings = append(metricStrings, metricStrings = append(metricStrings,
fmt.Sprintf("%s{%s} => %s", fmt.Sprintf("%s{%s} => %s",
metricName, metricName,
strings.Join(labelStrings, ","), strings.Join(labelStrings, ", "),
strings.Join(valueStrings, ", "))) strings.Join(valueStrings, ", ")))
} }
sort.Strings(metricStrings) sort.Strings(metricStrings)
@ -279,7 +266,7 @@ func (node *MatrixLiteral) NodeTreeToDotGraph() string {
} }
func (node *StringLiteral) NodeTreeToDotGraph() string { func (node *StringLiteral) NodeTreeToDotGraph() string {
return fmt.Sprintf("%#p[label=\"'%s'\"];\n", node, node.str) return fmt.Sprintf("%#p[label=\"'%q'\"];\n", node, node.str)
} }
func (node *StringFunctionCall) NodeTreeToDotGraph() string { func (node *StringFunctionCall) NodeTreeToDotGraph() string {
@ -316,11 +303,17 @@ func (node *VectorLiteral) String() string {
labelStrings := make([]string, 0, len(node.labels)-1) labelStrings := make([]string, 0, len(node.labels)-1)
for label, value := range node.labels { for label, value := range node.labels {
if label != model.MetricNameLabel { if label != model.MetricNameLabel {
labelStrings = append(labelStrings, fmt.Sprintf("%s='%s'", label, value)) labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))
} }
} }
switch len(labelStrings) {
case 0:
return string(metricName)
default:
sort.Strings(labelStrings) sort.Strings(labelStrings)
return fmt.Sprintf("%s{%s}", metricName, strings.Join(labelStrings, ",")) return fmt.Sprintf("%s{%s}", metricName, strings.Join(labelStrings, ","))
}
} }
func (node *VectorFunctionCall) String() string { func (node *VectorFunctionCall) String() string {
@ -342,7 +335,7 @@ func (node *MatrixLiteral) String() string {
} }
func (node *StringLiteral) String() string { func (node *StringLiteral) String() string {
return fmt.Sprintf("'%s'", node.str) return fmt.Sprintf("%q", node.str)
} }
func (node *StringFunctionCall) String() string { func (node *StringFunctionCall) String() string {

View File

@ -30,6 +30,7 @@ type Result struct {
type RuleManager interface { type RuleManager interface {
AddRulesFromConfig(config config.Config) error AddRulesFromConfig(config config.Config) error
Run() Run()
Rules() []Rule
} }
type ruleManager struct { type ruleManager struct {
@ -117,3 +118,12 @@ func (m *ruleManager) AddRulesFromConfig(config config.Config) error {
} }
return nil return nil
} }
func (m *ruleManager) Rules() []Rule {
m.Lock()
defer m.Unlock()
rules := make([]Rule, len(m.rules))
copy(rules, m.rules)
return rules
}

View File

@ -109,252 +109,252 @@ func TestExpressions(t *testing.T) {
intervalRanges int intervalRanges int
}{ }{
{ {
expr: "SUM(http_requests)", expr: `SUM(http_requests)`,
output: []string{"http_requests{} => 3600 @[%v]"}, output: []string{`http_requests => 3600 @[%v]`},
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "SUM(http_requests) BY (job)", expr: `SUM(http_requests) BY (job)`,
output: []string{ output: []string{
"http_requests{job='api-server'} => 1000 @[%v]", `http_requests{job="api-server"} => 1000 @[%v]`,
"http_requests{job='app-server'} => 2600 @[%v]", `http_requests{job="app-server"} => 2600 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "COUNT(http_requests) BY (job)", expr: `COUNT(http_requests) BY (job)`,
output: []string{ output: []string{
"http_requests{job='api-server'} => 4 @[%v]", `http_requests{job="api-server"} => 4 @[%v]`,
"http_requests{job='app-server'} => 4 @[%v]", `http_requests{job="app-server"} => 4 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "SUM(http_requests) BY (job, group)", expr: `SUM(http_requests) BY (job, group)`,
output: []string{ output: []string{
"http_requests{group='canary',job='api-server'} => 700 @[%v]", `http_requests{group="canary", job="api-server"} => 700 @[%v]`,
"http_requests{group='canary',job='app-server'} => 1500 @[%v]", `http_requests{group="canary", job="app-server"} => 1500 @[%v]`,
"http_requests{group='production',job='api-server'} => 300 @[%v]", `http_requests{group="production", job="api-server"} => 300 @[%v]`,
"http_requests{group='production',job='app-server'} => 1100 @[%v]", `http_requests{group="production", job="app-server"} => 1100 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "AVG(http_requests) BY (job)", expr: `AVG(http_requests) BY (job)`,
output: []string{ output: []string{
"http_requests{job='api-server'} => 250 @[%v]", `http_requests{job="api-server"} => 250 @[%v]`,
"http_requests{job='app-server'} => 650 @[%v]", `http_requests{job="app-server"} => 650 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "MIN(http_requests) BY (job)", expr: `MIN(http_requests) BY (job)`,
output: []string{ output: []string{
"http_requests{job='api-server'} => 100 @[%v]", `http_requests{job="api-server"} => 100 @[%v]`,
"http_requests{job='app-server'} => 500 @[%v]", `http_requests{job="app-server"} => 500 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "MAX(http_requests) BY (job)", expr: `MAX(http_requests) BY (job)`,
output: []string{ output: []string{
"http_requests{job='api-server'} => 400 @[%v]", `http_requests{job="api-server"} => 400 @[%v]`,
"http_requests{job='app-server'} => 800 @[%v]", `http_requests{job="app-server"} => 800 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "SUM(http_requests) BY (job) - COUNT(http_requests) BY (job)", expr: `SUM(http_requests) BY (job) - COUNT(http_requests) BY (job)`,
output: []string{ output: []string{
"http_requests{job='api-server'} => 996 @[%v]", `http_requests{job="api-server"} => 996 @[%v]`,
"http_requests{job='app-server'} => 2596 @[%v]", `http_requests{job="app-server"} => 2596 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "SUM(http_requests) BY (job) - 2", expr: `SUM(http_requests) BY (job) - 2`,
output: []string{ output: []string{
"http_requests{job='api-server'} => 998 @[%v]", `http_requests{job="api-server"} => 998 @[%v]`,
"http_requests{job='app-server'} => 2598 @[%v]", `http_requests{job="app-server"} => 2598 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "SUM(http_requests) BY (job) % 3", expr: `SUM(http_requests) BY (job) % 3`,
output: []string{ output: []string{
"http_requests{job='api-server'} => 1 @[%v]", `http_requests{job="api-server"} => 1 @[%v]`,
"http_requests{job='app-server'} => 2 @[%v]", `http_requests{job="app-server"} => 2 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "SUM(http_requests) BY (job) / 0", expr: `SUM(http_requests) BY (job) / 0`,
output: []string{ output: []string{
"http_requests{job='api-server'} => +Inf @[%v]", `http_requests{job="api-server"} => +Inf @[%v]`,
"http_requests{job='app-server'} => +Inf @[%v]", `http_requests{job="app-server"} => +Inf @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "SUM(http_requests) BY (job) > 1000", expr: `SUM(http_requests) BY (job) > 1000`,
output: []string{ output: []string{
"http_requests{job='app-server'} => 2600 @[%v]", `http_requests{job="app-server"} => 2600 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "SUM(http_requests) BY (job) <= 1000", expr: `SUM(http_requests) BY (job) <= 1000`,
output: []string{ output: []string{
"http_requests{job='api-server'} => 1000 @[%v]", `http_requests{job="api-server"} => 1000 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "SUM(http_requests) BY (job) != 1000", expr: `SUM(http_requests) BY (job) != 1000`,
output: []string{ output: []string{
"http_requests{job='app-server'} => 2600 @[%v]", `http_requests{job="app-server"} => 2600 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "SUM(http_requests) BY (job) == 1000", expr: `SUM(http_requests) BY (job) == 1000`,
output: []string{ output: []string{
"http_requests{job='api-server'} => 1000 @[%v]", `http_requests{job="api-server"} => 1000 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "SUM(http_requests) BY (job) + SUM(http_requests) BY (job)", expr: `SUM(http_requests) BY (job) + SUM(http_requests) BY (job)`,
output: []string{ output: []string{
"http_requests{job='api-server'} => 2000 @[%v]", `http_requests{job="api-server"} => 2000 @[%v]`,
"http_requests{job='app-server'} => 5200 @[%v]", `http_requests{job="app-server"} => 5200 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "http_requests{job='api-server', group='canary'}", expr: `http_requests{job="api-server", group="canary"}`,
output: []string{ output: []string{
"http_requests{group='canary',instance='0',job='api-server'} => 300 @[%v]", `http_requests{group="canary", instance="0", job="api-server"} => 300 @[%v]`,
"http_requests{group='canary',instance='1',job='api-server'} => 400 @[%v]", `http_requests{group="canary", instance="1", job="api-server"} => 400 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 2, intervalRanges: 2,
}, { }, {
expr: "http_requests{job='api-server', group='canary'} + delta(http_requests{job='api-server'}[5m], 1)", expr: `http_requests{job="api-server", group="canary"} + delta(http_requests{job="api-server"}[5m], 1)`,
output: []string{ output: []string{
"http_requests{group='canary',instance='0',job='api-server'} => 330 @[%v]", `http_requests{group="canary", instance="0", job="api-server"} => 330 @[%v]`,
"http_requests{group='canary',instance='1',job='api-server'} => 440 @[%v]", `http_requests{group="canary", instance="1", job="api-server"} => 440 @[%v]`,
}, },
fullRanges: 4, fullRanges: 4,
intervalRanges: 0, intervalRanges: 0,
}, { }, {
expr: "delta(http_requests[25m], 1)", expr: `delta(http_requests[25m], 1)`,
output: []string{ output: []string{
"http_requests{group='canary',instance='0',job='api-server'} => 150 @[%v]", `http_requests{group="canary", instance="0", job="api-server"} => 150 @[%v]`,
"http_requests{group='canary',instance='0',job='app-server'} => 350 @[%v]", `http_requests{group="canary", instance="0", job="app-server"} => 350 @[%v]`,
"http_requests{group='canary',instance='1',job='api-server'} => 200 @[%v]", `http_requests{group="canary", instance="1", job="api-server"} => 200 @[%v]`,
"http_requests{group='canary',instance='1',job='app-server'} => 400 @[%v]", `http_requests{group="canary", instance="1", job="app-server"} => 400 @[%v]`,
"http_requests{group='production',instance='0',job='api-server'} => 50 @[%v]", `http_requests{group="production", instance="0", job="api-server"} => 50 @[%v]`,
"http_requests{group='production',instance='0',job='app-server'} => 250 @[%v]", `http_requests{group="production", instance="0", job="app-server"} => 250 @[%v]`,
"http_requests{group='production',instance='1',job='api-server'} => 100 @[%v]", `http_requests{group="production", instance="1", job="api-server"} => 100 @[%v]`,
"http_requests{group='production',instance='1',job='app-server'} => 300 @[%v]", `http_requests{group="production", instance="1", job="app-server"} => 300 @[%v]`,
}, },
fullRanges: 8, fullRanges: 8,
intervalRanges: 0, intervalRanges: 0,
}, { }, {
expr: "sort(http_requests)", expr: `sort(http_requests)`,
output: []string{ output: []string{
"http_requests{group='production',instance='0',job='api-server'} => 100 @[%v]", `http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`,
"http_requests{group='production',instance='1',job='api-server'} => 200 @[%v]", `http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
"http_requests{group='canary',instance='0',job='api-server'} => 300 @[%v]", `http_requests{group="canary", instance="0", job="api-server"} => 300 @[%v]`,
"http_requests{group='canary',instance='1',job='api-server'} => 400 @[%v]", `http_requests{group="canary", instance="1", job="api-server"} => 400 @[%v]`,
"http_requests{group='production',instance='0',job='app-server'} => 500 @[%v]", `http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`,
"http_requests{group='production',instance='1',job='app-server'} => 600 @[%v]", `http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`,
"http_requests{group='canary',instance='0',job='app-server'} => 700 @[%v]", `http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`,
"http_requests{group='canary',instance='1',job='app-server'} => 800 @[%v]", `http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`,
}, },
checkOrder: true, checkOrder: true,
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
expr: "sort_desc(http_requests)", expr: `sort_desc(http_requests)`,
output: []string{ output: []string{
"http_requests{group='canary',instance='1',job='app-server'} => 800 @[%v]", `http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`,
"http_requests{group='canary',instance='0',job='app-server'} => 700 @[%v]", `http_requests{group="canary", instance="0", job="app-server"} => 700 @[%v]`,
"http_requests{group='production',instance='1',job='app-server'} => 600 @[%v]", `http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`,
"http_requests{group='production',instance='0',job='app-server'} => 500 @[%v]", `http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`,
"http_requests{group='canary',instance='1',job='api-server'} => 400 @[%v]", `http_requests{group="canary", instance="1", job="api-server"} => 400 @[%v]`,
"http_requests{group='canary',instance='0',job='api-server'} => 300 @[%v]", `http_requests{group="canary", instance="0", job="api-server"} => 300 @[%v]`,
"http_requests{group='production',instance='1',job='api-server'} => 200 @[%v]", `http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
"http_requests{group='production',instance='0',job='api-server'} => 100 @[%v]", `http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`,
}, },
checkOrder: true, checkOrder: true,
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
// Single-letter label names and values. // Single-letter label names and values.
expr: "x{y='testvalue'}", expr: `x{y="testvalue"}`,
output: []string{ output: []string{
"x{y='testvalue'} => 100 @[%v]", `x{y="testvalue"} => 100 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 1, intervalRanges: 1,
}, { }, {
// Lower-cased aggregation operators should work too. // Lower-cased aggregation operators should work too.
expr: "sum(http_requests) by (job) + min(http_requests) by (job) + max(http_requests) by (job) + avg(http_requests) by (job)", expr: `sum(http_requests) by (job) + min(http_requests) by (job) + max(http_requests) by (job) + avg(http_requests) by (job)`,
output: []string{ output: []string{
"http_requests{job='app-server'} => 4550 @[%v]", `http_requests{job="app-server"} => 4550 @[%v]`,
"http_requests{job='api-server'} => 1750 @[%v]", `http_requests{job="api-server"} => 1750 @[%v]`,
}, },
fullRanges: 0, fullRanges: 0,
intervalRanges: 8, intervalRanges: 8,
}, { }, {
// Deltas should be adjusted for target interval vs. samples under target interval. // Deltas should be adjusted for target interval vs. samples under target interval.
expr: "delta(http_requests{group='canary',instance='1',job='app-server'}[18m], 1)", expr: `delta(http_requests{group="canary", instance="1", job="app-server"}[18m], 1)`,
output: []string{"http_requests{group='canary',instance='1',job='app-server'} => 288 @[%v]"}, output: []string{`http_requests{group="canary", instance="1", job="app-server"} => 288 @[%v]`},
fullRanges: 1, fullRanges: 1,
intervalRanges: 0, intervalRanges: 0,
}, { }, {
// Rates should transform per-interval deltas to per-second rates. // Rates should transform per-interval deltas to per-second rates.
expr: "rate(http_requests{group='canary',instance='1',job='app-server'}[10m])", expr: `rate(http_requests{group="canary", instance="1", job="app-server"}[10m])`,
output: []string{"http_requests{group='canary',instance='1',job='app-server'} => 0.26666666666666666 @[%v]"}, output: []string{`http_requests{group="canary", instance="1", job="app-server"} => 0.26666666666666666 @[%v]`},
fullRanges: 1, fullRanges: 1,
intervalRanges: 0, intervalRanges: 0,
}, { }, {
// Counter resets in middle of range are ignored by delta() if counter == 1. // Counter resets in middle of range are ignored by delta() if counter == 1.
expr: "delta(testcounter_reset_middle[50m], 1)", expr: `delta(testcounter_reset_middle[50m], 1)`,
output: []string{"testcounter_reset_middle{} => 90 @[%v]"}, output: []string{`testcounter_reset_middle => 90 @[%v]`},
fullRanges: 1, fullRanges: 1,
intervalRanges: 0, intervalRanges: 0,
}, { }, {
// Counter resets in middle of range are not ignored by delta() if counter == 0. // Counter resets in middle of range are not ignored by delta() if counter == 0.
expr: "delta(testcounter_reset_middle[50m], 0)", expr: `delta(testcounter_reset_middle[50m], 0)`,
output: []string{"testcounter_reset_middle{} => 50 @[%v]"}, output: []string{`testcounter_reset_middle => 50 @[%v]`},
fullRanges: 1, fullRanges: 1,
intervalRanges: 0, intervalRanges: 0,
}, { }, {
// Counter resets at end of range are ignored by delta() if counter == 1. // Counter resets at end of range are ignored by delta() if counter == 1.
expr: "delta(testcounter_reset_end[5m], 1)", expr: `delta(testcounter_reset_end[5m], 1)`,
output: []string{"testcounter_reset_end{} => 0 @[%v]"}, output: []string{`testcounter_reset_end => 0 @[%v]`},
fullRanges: 1, fullRanges: 1,
intervalRanges: 0, intervalRanges: 0,
}, { }, {
// Counter resets at end of range are not ignored by delta() if counter == 0. // Counter resets at end of range are not ignored by delta() if counter == 0.
expr: "delta(testcounter_reset_end[5m], 0)", expr: `delta(testcounter_reset_end[5m], 0)`,
output: []string{"testcounter_reset_end{} => -90 @[%v]"}, output: []string{`testcounter_reset_end => -90 @[%v]`},
fullRanges: 1, fullRanges: 1,
intervalRanges: 0, intervalRanges: 0,
}, { }, {
// Empty expressions shouldn't parse. // Empty expressions shouldn"t parse.
expr: "", expr: ``,
shouldFail: true, shouldFail: true,
}, { }, {
// Subtracting a vector from a scalar is not supported. // Subtracting a vector from a scalar is not supported.
expr: "1 - http_requests", expr: `1 - http_requests`,
shouldFail: true, shouldFail: true,
}, { }, {
// Interval durations can't be in quotes. // Interval durations can"t be in quotes.
expr: "http_requests['1m']", expr: `http_requests["1m"]`,
shouldFail: true, shouldFail: true,
}, },
} }
@ -498,18 +498,18 @@ func TestAlertingRule(t *testing.T) {
// Labels in expected output need to be alphabetically sorted. // Labels in expected output need to be alphabetically sorted.
var evalOutputs = [][]string{ var evalOutputs = [][]string{
{ {
"ALERTS{alertname='HttpRequestRateLow',alertstate='pending',group='canary',instance='0',job='app-server'} => 1 @[%v]", `ALERTS{alertname="HttpRequestRateLow", alertstate="pending", group="canary", instance="0", job="app-server"} => 1 @[%v]`,
"ALERTS{alertname='HttpRequestRateLow',alertstate='pending',group='canary',instance='1',job='app-server'} => 1 @[%v]", `ALERTS{alertname="HttpRequestRateLow", alertstate="pending", group="canary", instance="1", job="app-server"} => 1 @[%v]`,
}, },
{ {
"ALERTS{alertname='HttpRequestRateLow',alertstate='pending',group='canary',instance='0',job='app-server'} => 0 @[%v]", `ALERTS{alertname="HttpRequestRateLow", alertstate="pending", group="canary", instance="0", job="app-server"} => 0 @[%v]`,
"ALERTS{alertname='HttpRequestRateLow',alertstate='firing',group='canary',instance='0',job='app-server'} => 1 @[%v]", `ALERTS{alertname="HttpRequestRateLow", alertstate="firing", group="canary", instance="0", job="app-server"} => 1 @[%v]`,
"ALERTS{alertname='HttpRequestRateLow',alertstate='pending',group='canary',instance='1',job='app-server'} => 0 @[%v]", `ALERTS{alertname="HttpRequestRateLow", alertstate="pending", group="canary", instance="1", job="app-server"} => 0 @[%v]`,
"ALERTS{alertname='HttpRequestRateLow',alertstate='firing',group='canary',instance='1',job='app-server'} => 1 @[%v]", `ALERTS{alertname="HttpRequestRateLow", alertstate="firing", group="canary", instance="1", job="app-server"} => 1 @[%v]`,
}, },
{ {
"ALERTS{alertname='HttpRequestRateLow',alertstate='firing',group='canary',instance='1',job='app-server'} => 0 @[%v]", `ALERTS{alertname="HttpRequestRateLow", alertstate="firing", group="canary", instance="1", job="app-server"} => 0 @[%v]`,
"ALERTS{alertname='HttpRequestRateLow',alertstate='firing',group='canary',instance='0',job='app-server'} => 0 @[%v]", `ALERTS{alertname="HttpRequestRateLow", alertstate="firing", group="canary", instance="0", job="app-server"} => 0 @[%v]`,
}, },
{ {
/* empty */ /* empty */
@ -523,7 +523,7 @@ func TestAlertingRule(t *testing.T) {
defer closer.Close() defer closer.Close()
tieredStorage.Flush() tieredStorage.Flush()
alertExpr, err := LoadExprFromString("http_requests{group='canary',job='app-server'} < 100") alertExpr, err := LoadExprFromString(`http_requests{group="canary", job="app-server"} < 100`)
if err != nil { if err != nil {
t.Fatalf("Unable to parse alert expression: %s", err) t.Fatalf("Unable to parse alert expression: %s", err)
} }

View File

@ -15,6 +15,7 @@ package web
import ( import (
"github.com/prometheus/prometheus/retrieval" "github.com/prometheus/prometheus/retrieval"
"github.com/prometheus/prometheus/rules"
"github.com/prometheus/prometheus/storage/metric" "github.com/prometheus/prometheus/storage/metric"
"net/http" "net/http"
"sync" "sync"
@ -26,7 +27,7 @@ type PrometheusStatus struct {
Config string Config string
Curation metric.CurationState Curation metric.CurationState
Flags map[string]string Flags map[string]string
Rules string Rules []rules.Rule
TargetPools map[string]*retrieval.TargetPool TargetPools map[string]*retrieval.TargetPool
Birth time.Time Birth time.Time

View File

@ -35,7 +35,7 @@
<h2>Rules</h2> <h2>Rules</h2>
<div class="grouping_box"> <div class="grouping_box">
{{.Rules}} <pre>{{range .Rules}}{{.String}}{{end}}</pre>
</div> </div>
<h2>Targets</h2> <h2>Targets</h2>