Implement new alerting rule syntax

This commit is contained in:
Fabian Reinartz 2015-12-11 17:02:34 +01:00
parent a8c0307db4
commit af3a6661ed
6 changed files with 59 additions and 93 deletions

View file

@ -59,9 +59,7 @@ type AlertStmt struct {
Expr Expr Expr Expr
Duration time.Duration Duration time.Duration
Labels model.LabelSet Labels model.LabelSet
Summary string Annotations model.LabelSet
Description string
Runbook string
} }
// EvalStmt holds an expression and information on the range it should // EvalStmt holds an expression and information on the range it should

View file

@ -154,9 +154,7 @@ const (
itemIf itemIf
itemFor itemFor
itemWith itemWith
itemSummary itemAnnotations
itemRunbook
itemDescription
itemKeepCommon itemKeepCommon
itemOffset itemOffset
itemBy itemBy
@ -186,9 +184,7 @@ var key = map[string]itemType{
"if": itemIf, "if": itemIf,
"for": itemFor, "for": itemFor,
"with": itemWith, "with": itemWith,
"summary": itemSummary, "annotations": itemAnnotations,
"runbook": itemRunbook,
"description": itemDescription,
"offset": itemOffset, "offset": itemOffset,
"by": itemBy, "by": itemBy,
"keeping_extra": itemKeepCommon, "keeping_extra": itemKeepCommon,

View file

@ -241,14 +241,8 @@ var tests = []struct {
input: "with", input: "with",
expected: []item{{itemWith, 0, "with"}}, expected: []item{{itemWith, 0, "with"}},
}, { }, {
input: "description", input: "annotations",
expected: []item{{itemDescription, 0, "description"}}, expected: []item{{itemAnnotations, 0, "annotations"}},
}, {
input: "summary",
expected: []item{{itemSummary, 0, "summary"}},
}, {
input: "runbook",
expected: []item{{itemRunbook, 0, "runbook"}},
}, { }, {
input: "offset", input: "offset",
expected: []item{{itemOffset, 0, "offset"}}, expected: []item{{itemOffset, 0, "offset"}},

View file

@ -357,9 +357,9 @@ func (p *parser) stmt() Statement {
// alertStmt parses an alert rule. // alertStmt parses an alert rule.
// //
// ALERT name IF expr [FOR duration] [WITH label_set] // ALERT name IF expr [FOR duration]
// SUMMARY "summary" // [WITH label_set]
// DESCRIPTION "description" // [ANNOTATIONS label_set]
// //
func (p *parser) alertStmt() *AlertStmt { func (p *parser) alertStmt() *AlertStmt {
const ctx = "alert statement" const ctx = "alert statement"
@ -389,44 +389,10 @@ func (p *parser) alertStmt() *AlertStmt {
lset = p.labelSet() lset = p.labelSet()
} }
var ( annotations := model.LabelSet{}
hasSum, hasDesc, hasRunbook bool if p.peek().typ == itemAnnotations {
sum, desc, runbook string p.expect(itemAnnotations, ctx)
) annotations = p.labelSet()
Loop:
for {
switch p.next().typ {
case itemSummary:
if hasSum {
p.errorf("summary must not be defined twice")
}
hasSum = true
sum = p.unquoteString(p.expect(itemString, ctx).val)
case itemDescription:
if hasDesc {
p.errorf("description must not be defined twice")
}
hasDesc = true
desc = p.unquoteString(p.expect(itemString, ctx).val)
case itemRunbook:
if hasRunbook {
p.errorf("runbook must not be defined twice")
}
hasRunbook = true
runbook = p.unquoteString(p.expect(itemString, ctx).val)
default:
p.backup()
break Loop
}
}
if sum == "" {
p.errorf("alert summary missing")
}
if desc == "" {
p.errorf("alert description missing")
} }
return &AlertStmt{ return &AlertStmt{
@ -434,9 +400,7 @@ Loop:
Expr: expr, Expr: expr,
Duration: duration, Duration: duration,
Labels: lset, Labels: lset,
Summary: sum, Annotations: annotations,
Description: desc,
Runbook: runbook,
} }
} }
@ -874,11 +838,20 @@ func (p *parser) labelMatchers(operators ...itemType) metric.LabelMatchers {
matchers = append(matchers, m) matchers = append(matchers, m)
if p.peek().typ == itemIdentifier {
p.errorf("missing comma before next identifier %q", p.peek().val)
}
// Terminate list if last matcher. // Terminate list if last matcher.
if p.peek().typ != itemComma { if p.peek().typ != itemComma {
break break
} }
p.next() p.next()
// Allow comma after each item in a multi-line listing.
if p.peek().typ == itemRightBrace {
break
}
} }
p.expect(itemRightBrace, ctx) p.expect(itemRightBrace, ctx)

View file

@ -1153,15 +1153,19 @@ var testStatement = []struct {
service = "testservice" service = "testservice"
# ... more fields here ... # ... more fields here ...
} }
SUMMARY "Global request rate low" ANNOTATIONS {
DESCRIPTION "The global request rate is low" summary = "Global request rate low",
description = "The global request rate is low"
}
foo = bar{label1="value1"} foo = bar{label1="value1"}
ALERT BazAlert IF foo > 10 ALERT BazAlert IF foo > 10
DESCRIPTION "BazAlert" ANNOTATIONS {
RUNBOOK "http://my.url" description = "BazAlert",
SUMMARY "Baz" runbook = "http://my.url",
summary = "Baz",
}
`, `,
expected: Statements{ expected: Statements{
&RecordStmt{ &RecordStmt{
@ -1196,10 +1200,12 @@ var testStatement = []struct {
}, },
RHS: &NumberLiteral{10000}, RHS: &NumberLiteral{10000},
}}, }},
Labels: model.LabelSet{"service": "testservice"}, Labels: model.LabelSet{"service": "testservice"},
Duration: 5 * time.Minute, Duration: 5 * time.Minute,
Summary: "Global request rate low", Annotations: model.LabelSet{
Description: "The global request rate is low", "summary": "Global request rate low",
"description": "The global request rate is low",
},
}, },
&RecordStmt{ &RecordStmt{
Name: "foo", Name: "foo",
@ -1224,10 +1230,12 @@ var testStatement = []struct {
}, },
RHS: &NumberLiteral{10}, RHS: &NumberLiteral{10},
}, },
Labels: model.LabelSet{}, Labels: model.LabelSet{},
Summary: "Baz", Annotations: model.LabelSet{
Description: "BazAlert", "summary": "Baz",
Runbook: "http://my.url", "description": "BazAlert",
"runbook": "http://my.url",
},
}, },
}, },
}, { }, {
@ -1248,8 +1256,10 @@ var testStatement = []struct {
}, },
}, { }, {
input: `ALERT SomeName IF some_metric > 1 input: `ALERT SomeName IF some_metric > 1
SUMMARY "Global request rate low" ANNOTATIONS {
DESCRIPTION "The global request rate is low" summary = "Global request rate low",
description = "The global request rate is low"
}
`, `,
expected: Statements{ expected: Statements{
&AlertStmt{ &AlertStmt{
@ -1264,9 +1274,11 @@ var testStatement = []struct {
}, },
RHS: &NumberLiteral{1}, RHS: &NumberLiteral{1},
}, },
Labels: model.LabelSet{}, Labels: model.LabelSet{},
Summary: "Global request rate low", Annotations: model.LabelSet{
Description: "The global request rate is low", "summary": "Global request rate low",
"description": "The global request rate is low",
},
}, },
}, },
}, { }, {
@ -1276,8 +1288,10 @@ var testStatement = []struct {
service = "testservice" service = "testservice"
# ... more fields here ... # ... more fields here ...
} }
SUMMARY "Global request rate low" ANNOTATIONS {
DESCRIPTION "The global request rate is low" summary = "Global request rate low"
description = "The global request rate is low"
}
`, `,
fail: true, fail: true,
}, { }, {
@ -1323,16 +1337,6 @@ var testStatement = []struct {
DESCRIPTION "The global request rate is low" DESCRIPTION "The global request rate is low"
`, `,
fail: true, fail: true,
}, {
input: `ALERT SomeName IF some_metric > 1 WITH {}
SUMMARY "Global request rate low"
`,
fail: true,
}, {
input: `ALERT SomeName IF some_metric > 1
DESCRIPTION "The global request rate is low"
`,
fail: true,
}, },
// Fuzzing regression tests. // Fuzzing regression tests.
{ {

View file

@ -109,8 +109,9 @@ func (node *AlertStmt) String() string {
if len(node.Labels) > 0 { if len(node.Labels) > 0 {
s += fmt.Sprintf("\n\tWITH %s", node.Labels) s += fmt.Sprintf("\n\tWITH %s", node.Labels)
} }
s += fmt.Sprintf("\n\tSUMMARY %q", node.Summary) if len(node.Annotations) > 0 {
s += fmt.Sprintf("\n\tDESCRIPTION %q", node.Description) s += fmt.Sprintf("\n\tANNOTATIONS %s", node.Labels)
}
return s return s
} }