Merge pull request #1339 from prometheus/range-parsing

Consolidate offset modifier parsing
This commit is contained in:
Tobias Schmidt 2016-01-25 10:52:17 -05:00
commit 40ab0da398
2 changed files with 46 additions and 38 deletions

View file

@ -598,6 +598,21 @@ func (p *parser) unaryExpr() Expr {
} }
e = p.rangeSelector(vs) e = p.rangeSelector(vs)
} }
// Parse optional offset.
if p.peek().typ == itemOffset {
offset := p.offset()
switch s := e.(type) {
case *VectorSelector:
s.Offset = offset
case *MatrixSelector:
s.Offset = offset
default:
p.errorf("offset modifier must be preceded by a metric or range selector, but follows a %T instead", e)
}
}
return e return e
} }
@ -609,7 +624,7 @@ func (p *parser) rangeSelector(vs *VectorSelector) *MatrixSelector {
const ctx = "matrix selector" const ctx = "matrix selector"
p.next() p.next()
var erange, offset time.Duration var erange time.Duration
var err error var err error
erangeStr := p.expect(itemDuration, ctx).val erangeStr := p.expect(itemDuration, ctx).val
@ -620,27 +635,15 @@ func (p *parser) rangeSelector(vs *VectorSelector) *MatrixSelector {
p.expect(itemRightBracket, ctx) p.expect(itemRightBracket, ctx)
// Parse optional offset.
if p.peek().typ == itemOffset {
p.next()
offi := p.expect(itemDuration, ctx)
offset, err = parseDuration(offi.val)
if err != nil {
p.error(err)
}
}
e := &MatrixSelector{ e := &MatrixSelector{
Name: vs.Name, Name: vs.Name,
LabelMatchers: vs.LabelMatchers, LabelMatchers: vs.LabelMatchers,
Range: erange, Range: erange,
Offset: offset,
} }
return e return e
} }
// parseNumber parses a number. // number parses a number.
func (p *parser) number(val string) float64 { func (p *parser) number(val string) float64 {
n, err := strconv.ParseInt(val, 0, 64) n, err := strconv.ParseInt(val, 0, 64)
f := float64(n) f := float64(n)
@ -927,10 +930,28 @@ func (p *parser) metric() model.Metric {
return m return m
} }
// metricSelector parses a new metric selector. // offset parses an offset modifier.
// //
// <metric_identifier> [<label_matchers>] [ offset <duration> ] // offset <duration>
// [<metric_identifier>] <label_matchers> [ offset <duration> ] //
func (p *parser) offset() time.Duration {
const ctx = "offset"
p.next()
offi := p.expect(itemDuration, ctx)
offset, err := parseDuration(offi.val)
if err != nil {
p.error(err)
}
return offset
}
// vectorSelector parses a new vector selector.
//
// <metric_identifier> [<label_matchers>]
// [<metric_identifier>] <label_matchers>
// //
func (p *parser) vectorSelector(name string) *VectorSelector { func (p *parser) vectorSelector(name string) *VectorSelector {
const ctx = "metric selector" const ctx = "metric selector"
@ -978,22 +999,9 @@ func (p *parser) vectorSelector(name string) *VectorSelector {
p.errorf("vector selector must contain at least one non-empty matcher") p.errorf("vector selector must contain at least one non-empty matcher")
} }
var err error
var offset time.Duration
// Parse optional offset.
if p.peek().typ == itemOffset {
p.next()
offi := p.expect(itemDuration, ctx)
offset, err = parseDuration(offi.val)
if err != nil {
p.error(err)
}
}
return &VectorSelector{ return &VectorSelector{
Name: name, Name: name,
LabelMatchers: matchers, LabelMatchers: matchers,
Offset: offset,
} }
} }

View file

@ -758,7 +758,7 @@ var testExpr = []struct {
}, { }, {
input: `some_metric[5m] OFFSET 1`, input: `some_metric[5m] OFFSET 1`,
fail: true, fail: true,
errMsg: "unexpected number \"1\" in matrix selector, expected duration", errMsg: "unexpected number \"1\" in offset, expected duration",
}, { }, {
input: `some_metric[5m] OFFSET 1mm`, input: `some_metric[5m] OFFSET 1mm`,
fail: true, fail: true,
@ -766,7 +766,11 @@ var testExpr = []struct {
}, { }, {
input: `some_metric[5m] OFFSET`, input: `some_metric[5m] OFFSET`,
fail: true, fail: true,
errMsg: "unexpected end of input in matrix selector, expected duration", errMsg: "unexpected end of input in offset, expected duration",
}, {
input: `some_metric OFFSET 1m[5m]`,
fail: true,
errMsg: "could not parse remaining input \"[5m]\"...",
}, { }, {
input: `(foo + bar)[5m]`, input: `(foo + bar)[5m]`,
fail: true, fail: true,
@ -900,10 +904,6 @@ var testExpr = []struct {
input: `sum (some_metric) by test`, input: `sum (some_metric) by test`,
fail: true, fail: true,
errMsg: "unexpected identifier \"test\" in grouping opts, expected \"(\"", errMsg: "unexpected identifier \"test\" in grouping opts, expected \"(\"",
}, {
input: `some_metric[5m] OFFSET`,
fail: true,
errMsg: "unexpected end of input in matrix selector, expected duration",
}, { }, {
input: `sum () by (test)`, input: `sum () by (test)`,
fail: true, fail: true,
@ -1147,7 +1147,7 @@ var testStatement = []struct {
input: ` input: `
# A simple test recording rule. # A simple test recording rule.
dc:http_request:rate5m = sum(rate(http_request_count[5m])) by (dc) dc:http_request:rate5m = sum(rate(http_request_count[5m])) by (dc)
# A simple test alerting rule. # A simple test alerting rule.
ALERT GlobalRequestRateLow IF(dc:http_request:rate5m < 10000) FOR 5m ALERT GlobalRequestRateLow IF(dc:http_request:rate5m < 10000) FOR 5m
LABELS { LABELS {
@ -1315,7 +1315,7 @@ var testStatement = []struct {
ALERT GlobalRequestRateLow IF(dc:http_request:rate5m < 10000) FOR 5 ALERT GlobalRequestRateLow IF(dc:http_request:rate5m < 10000) FOR 5
LABELS { LABELS {
service = "testservice" service = "testservice"
# ... more fields here ... # ... more fields here ...
} }
ANNOTATIONS { ANNOTATIONS {
summary = "Global request rate low" summary = "Global request rate low"