This commit removes all functionality from rules/ that is now handled in promql/. All parts of Prometheus are changed to use the promql/ package.pull/655/head
parent
5602328c7c
commit
3ca11bcaf5
@ -1,27 +0,0 @@ |
||||
# Copyright 2013 The Prometheus Authors
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
all: parser.y.go lexer.l.go |
||||
|
||||
include ../Makefile.INCLUDE |
||||
|
||||
parser.y.go: parser.y |
||||
$(GOCC) tool yacc -o parser.y.go -v "" parser.y
|
||||
|
||||
lexer.l.go: parser.y.go lexer.l |
||||
# This is golex from https://github.com/cznic/golex.
|
||||
$(GO_GET) github.com/cznic/golex
|
||||
golex -o="lexer.l.go" lexer.l
|
||||
|
||||
clean: |
||||
rm lexer.l.go parser.y.go
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,752 +0,0 @@ |
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ast |
||||
|
||||
import ( |
||||
"container/heap" |
||||
"fmt" |
||||
"math" |
||||
"sort" |
||||
"strconv" |
||||
"time" |
||||
|
||||
clientmodel "github.com/prometheus/client_golang/model" |
||||
|
||||
"github.com/prometheus/prometheus/storage/metric" |
||||
) |
||||
|
||||
// Function represents a function of the expression language and is
|
||||
// used by function nodes.
|
||||
type Function struct { |
||||
name string |
||||
argTypes []ExprType |
||||
optionalArgs int |
||||
returnType ExprType |
||||
callFn func(timestamp clientmodel.Timestamp, args []Node) interface{} |
||||
} |
||||
|
||||
// CheckArgTypes returns a non-nil error if the number or types of
|
||||
// passed in arg nodes do not match the function's expectations.
|
||||
func (function *Function) CheckArgTypes(args []Node) error { |
||||
if len(function.argTypes) < len(args) { |
||||
return fmt.Errorf( |
||||
"too many arguments to function %v(): %v expected at most, %v given", |
||||
function.name, len(function.argTypes), len(args), |
||||
) |
||||
} |
||||
if len(function.argTypes)-function.optionalArgs > len(args) { |
||||
return fmt.Errorf( |
||||
"too few arguments to function %v(): %v expected at least, %v given", |
||||
function.name, len(function.argTypes)-function.optionalArgs, len(args), |
||||
) |
||||
} |
||||
for idx, arg := range args { |
||||
invalidType := false |
||||
var expectedType string |
||||
if _, ok := arg.(ScalarNode); function.argTypes[idx] == ScalarType && !ok { |
||||
invalidType = true |
||||
expectedType = "scalar" |
||||
} |
||||
if _, ok := arg.(VectorNode); function.argTypes[idx] == VectorType && !ok { |
||||
invalidType = true |
||||
expectedType = "vector" |
||||
} |
||||
if _, ok := arg.(MatrixNode); function.argTypes[idx] == MatrixType && !ok { |
||||
invalidType = true |
||||
expectedType = "matrix" |
||||
} |
||||
if _, ok := arg.(StringNode); function.argTypes[idx] == StringType && !ok { |
||||
invalidType = true |
||||
expectedType = "string" |
||||
} |
||||
|
||||
if invalidType { |
||||
return fmt.Errorf( |
||||
"wrong type for argument %v in function %v(), expected %v", |
||||
idx, function.name, expectedType, |
||||
) |
||||
} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// === time() clientmodel.SampleValue ===
|
||||
func timeImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
return clientmodel.SampleValue(timestamp.Unix()) |
||||
} |
||||
|
||||
// === delta(matrix MatrixNode, isCounter=0 ScalarNode) Vector ===
|
||||
func deltaImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
matrixNode := args[0].(MatrixNode) |
||||
isCounter := len(args) >= 2 && args[1].(ScalarNode).Eval(timestamp) > 0 |
||||
resultVector := Vector{} |
||||
|
||||
// If we treat these metrics as counters, we need to fetch all values
|
||||
// in the interval to find breaks in the timeseries' monotonicity.
|
||||
// I.e. if a counter resets, we want to ignore that reset.
|
||||
var matrixValue Matrix |
||||
if isCounter { |
||||
matrixValue = matrixNode.Eval(timestamp) |
||||
} else { |
||||
matrixValue = matrixNode.EvalBoundaries(timestamp) |
||||
} |
||||
for _, samples := range matrixValue { |
||||
// No sense in trying to compute a delta without at least two points. Drop
|
||||
// this vector element.
|
||||
if len(samples.Values) < 2 { |
||||
continue |
||||
} |
||||
|
||||
counterCorrection := clientmodel.SampleValue(0) |
||||
lastValue := clientmodel.SampleValue(0) |
||||
for _, sample := range samples.Values { |
||||
currentValue := sample.Value |
||||
if isCounter && currentValue < lastValue { |
||||
counterCorrection += lastValue - currentValue |
||||
} |
||||
lastValue = currentValue |
||||
} |
||||
resultValue := lastValue - samples.Values[0].Value + counterCorrection |
||||
|
||||
targetInterval := args[0].(*MatrixSelector).interval |
||||
sampledInterval := samples.Values[len(samples.Values)-1].Timestamp.Sub(samples.Values[0].Timestamp) |
||||
if sampledInterval == 0 { |
||||
// Only found one sample. Cannot compute a rate from this.
|
||||
continue |
||||
} |
||||
// Correct for differences in target vs. actual delta interval.
|
||||
//
|
||||
// Above, we didn't actually calculate the delta for the specified target
|
||||
// interval, but for an interval between the first and last found samples
|
||||
// under the target interval, which will usually have less time between
|
||||
// them. Depending on how many samples are found under a target interval,
|
||||
// the delta results are distorted and temporal aliasing occurs (ugly
|
||||
// bumps). This effect is corrected for below.
|
||||
intervalCorrection := clientmodel.SampleValue(targetInterval) / clientmodel.SampleValue(sampledInterval) |
||||
resultValue *= intervalCorrection |
||||
|
||||
resultSample := &Sample{ |
||||
Metric: samples.Metric, |
||||
Value: resultValue, |
||||
Timestamp: timestamp, |
||||
} |
||||
resultSample.Metric.Delete(clientmodel.MetricNameLabel) |
||||
resultVector = append(resultVector, resultSample) |
||||
} |
||||
return resultVector |
||||
} |
||||
|
||||
// === rate(node MatrixNode) Vector ===
|
||||
func rateImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
args = append(args, &ScalarLiteral{value: 1}) |
||||
vector := deltaImpl(timestamp, args).(Vector) |
||||
|
||||
// TODO: could be other type of MatrixNode in the future (right now, only
|
||||
// MatrixSelector exists). Find a better way of getting the duration of a
|
||||
// matrix, such as looking at the samples themselves.
|
||||
interval := args[0].(*MatrixSelector).interval |
||||
for i := range vector { |
||||
vector[i].Value /= clientmodel.SampleValue(interval / time.Second) |
||||
} |
||||
return vector |
||||
} |
||||
|
||||
type vectorByValueHeap Vector |
||||
|
||||
func (s vectorByValueHeap) Len() int { |
||||
return len(s) |
||||
} |
||||
|
||||
func (s vectorByValueHeap) Less(i, j int) bool { |
||||
return s[i].Value < s[j].Value |
||||
} |
||||
|
||||
func (s vectorByValueHeap) Swap(i, j int) { |
||||
s[i], s[j] = s[j], s[i] |
||||
} |
||||
|
||||
func (s *vectorByValueHeap) Push(x interface{}) { |
||||
*s = append(*s, x.(*Sample)) |
||||
} |
||||
|
||||
func (s *vectorByValueHeap) Pop() interface{} { |
||||
old := *s |
||||
n := len(old) |
||||
el := old[n-1] |
||||
*s = old[0 : n-1] |
||||
return el |
||||
} |
||||
|
||||
type reverseHeap struct { |
||||
heap.Interface |
||||
} |
||||
|
||||
func (s reverseHeap) Less(i, j int) bool { |
||||
return s.Interface.Less(j, i) |
||||
} |
||||
|
||||
// === sort(node VectorNode) Vector ===
|
||||
func sortImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
byValueSorter := vectorByValueHeap(args[0].(VectorNode).Eval(timestamp)) |
||||
sort.Sort(byValueSorter) |
||||
return Vector(byValueSorter) |
||||
} |
||||
|
||||
// === sortDesc(node VectorNode) Vector ===
|
||||
func sortDescImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
byValueSorter := vectorByValueHeap(args[0].(VectorNode).Eval(timestamp)) |
||||
sort.Sort(sort.Reverse(byValueSorter)) |
||||
return Vector(byValueSorter) |
||||
} |
||||
|
||||
// === topk(k ScalarNode, node VectorNode) Vector ===
|
||||
func topkImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
k := int(args[0].(ScalarNode).Eval(timestamp)) |
||||
if k < 1 { |
||||
return Vector{} |
||||
} |
||||
|
||||
topk := make(vectorByValueHeap, 0, k) |
||||
vector := args[1].(VectorNode).Eval(timestamp) |
||||
|
||||
for _, el := range vector { |
||||
if len(topk) < k || topk[0].Value < el.Value { |
||||
if len(topk) == k { |
||||
heap.Pop(&topk) |
||||
} |
||||
heap.Push(&topk, el) |
||||
} |
||||
} |
||||
sort.Sort(sort.Reverse(topk)) |
||||
return Vector(topk) |
||||
} |
||||
|
||||
// === bottomk(k ScalarNode, node VectorNode) Vector ===
|
||||
func bottomkImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
k := int(args[0].(ScalarNode).Eval(timestamp)) |
||||
if k < 1 { |
||||
return Vector{} |
||||
} |
||||
|
||||
bottomk := make(vectorByValueHeap, 0, k) |
||||
bkHeap := reverseHeap{Interface: &bottomk} |
||||
vector := args[1].(VectorNode).Eval(timestamp) |
||||
|
||||
for _, el := range vector { |
||||
if len(bottomk) < k || bottomk[0].Value > el.Value { |
||||
if len(bottomk) == k { |
||||
heap.Pop(&bkHeap) |
||||
} |
||||
heap.Push(&bkHeap, el) |
||||
} |
||||
} |
||||
sort.Sort(bottomk) |
||||
return Vector(bottomk) |
||||
} |
||||
|
||||
// === drop_common_labels(node VectorNode) Vector ===
|
||||
func dropCommonLabelsImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
vector := args[0].(VectorNode).Eval(timestamp) |
||||
if len(vector) < 1 { |
||||
return Vector{} |
||||
} |
||||
common := clientmodel.LabelSet{} |
||||
for k, v := range vector[0].Metric.Metric { |
||||
// TODO(julius): Should we also drop common metric names?
|
||||
if k == clientmodel.MetricNameLabel { |
||||
continue |
||||
} |
||||
common[k] = v |
||||
} |
||||
|
||||
for _, el := range vector[1:] { |
||||
for k, v := range common { |
||||
if el.Metric.Metric[k] != v { |
||||
// Deletion of map entries while iterating over them is safe.
|
||||
// From http://golang.org/ref/spec#For_statements:
|
||||
// "If map entries that have not yet been reached are deleted during
|
||||
// iteration, the corresponding iteration values will not be produced."
|
||||
delete(common, k) |
||||
} |
||||
} |
||||
} |
||||
|
||||
for _, el := range vector { |
||||
for k := range el.Metric.Metric { |
||||
if _, ok := common[k]; ok { |
||||
el.Metric.Delete(k) |
||||
} |
||||
} |
||||
} |
||||
return vector |
||||
} |
||||
|
||||
// === round(vector VectorNode, toNearest=1 Scalar) Vector ===
|
||||
func roundImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
// round returns a number rounded to toNearest.
|
||||
// Ties are solved by rounding up.
|
||||
toNearest := float64(1) |
||||
if len(args) >= 2 { |
||||
toNearest = float64(args[1].(ScalarNode).Eval(timestamp)) |
||||
} |
||||
// Invert as it seems to cause fewer floating point accuracy issues.
|
||||
toNearestInverse := 1.0 / toNearest |
||||
|
||||
n := args[0].(VectorNode) |
||||
vector := n.Eval(timestamp) |
||||
for _, el := range vector { |
||||
el.Metric.Delete(clientmodel.MetricNameLabel) |
||||
el.Value = clientmodel.SampleValue(math.Floor(float64(el.Value)*toNearestInverse+0.5) / toNearestInverse) |
||||
} |
||||
return vector |
||||
} |
||||
|
||||
// === scalar(node VectorNode) Scalar ===
|
||||
func scalarImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
v := args[0].(VectorNode).Eval(timestamp) |
||||
if len(v) != 1 { |
||||
return clientmodel.SampleValue(math.NaN()) |
||||
} |
||||
return clientmodel.SampleValue(v[0].Value) |
||||
} |
||||
|
||||
// === count_scalar(vector VectorNode) model.SampleValue ===
|
||||
func countScalarImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
return clientmodel.SampleValue(len(args[0].(VectorNode).Eval(timestamp))) |
||||
} |
||||
|
||||
func aggrOverTime(timestamp clientmodel.Timestamp, args []Node, aggrFn func(metric.Values) clientmodel.SampleValue) interface{} { |
||||
n := args[0].(MatrixNode) |
||||
matrixVal := n.Eval(timestamp) |
||||
resultVector := Vector{} |
||||
|
||||
for _, el := range matrixVal { |
||||
if len(el.Values) == 0 { |
||||
continue |
||||
} |
||||
|
||||
el.Metric.Delete(clientmodel.MetricNameLabel) |
||||
resultVector = append(resultVector, &Sample{ |
||||
Metric: el.Metric, |
||||
Value: aggrFn(el.Values), |
||||
Timestamp: timestamp, |
||||
}) |
||||
} |
||||
return resultVector |
||||
} |
||||
|
||||
// === avg_over_time(matrix MatrixNode) Vector ===
|
||||
func avgOverTimeImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
return aggrOverTime(timestamp, args, func(values metric.Values) clientmodel.SampleValue { |
||||
var sum clientmodel.SampleValue |
||||
for _, v := range values { |
||||
sum += v.Value |
||||
} |
||||
return sum / clientmodel.SampleValue(len(values)) |
||||
}) |
||||
} |
||||
|
||||
// === count_over_time(matrix MatrixNode) Vector ===
|
||||
func countOverTimeImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
return aggrOverTime(timestamp, args, func(values metric.Values) clientmodel.SampleValue { |
||||
return clientmodel.SampleValue(len(values)) |
||||
}) |
||||
} |
||||
|
||||
// === floor(vector VectorNode) Vector ===
|
||||
func floorImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
n := args[0].(VectorNode) |
||||
vector := n.Eval(timestamp) |
||||
for _, el := range vector { |
||||
el.Metric.Delete(clientmodel.MetricNameLabel) |
||||
el.Value = clientmodel.SampleValue(math.Floor(float64(el.Value))) |
||||
} |
||||
return vector |
||||
} |
||||
|
||||
// === max_over_time(matrix MatrixNode) Vector ===
|
||||
func maxOverTimeImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
return aggrOverTime(timestamp, args, func(values metric.Values) clientmodel.SampleValue { |
||||
max := math.Inf(-1) |
||||
for _, v := range values { |
||||
max = math.Max(max, float64(v.Value)) |
||||
} |
||||
return clientmodel.SampleValue(max) |
||||
}) |
||||
} |
||||
|
||||
// === min_over_time(matrix MatrixNode) Vector ===
|
||||
func minOverTimeImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
return aggrOverTime(timestamp, args, func(values metric.Values) clientmodel.SampleValue { |
||||
min := math.Inf(1) |
||||
for _, v := range values { |
||||
min = math.Min(min, float64(v.Value)) |
||||
} |
||||
return clientmodel.SampleValue(min) |
||||
}) |
||||
} |
||||
|
||||
// === sum_over_time(matrix MatrixNode) Vector ===
|
||||
func sumOverTimeImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
return aggrOverTime(timestamp, args, func(values metric.Values) clientmodel.SampleValue { |
||||
var sum clientmodel.SampleValue |
||||
for _, v := range values { |
||||
sum += v.Value |
||||
} |
||||
return sum |
||||
}) |
||||
} |
||||
|
||||
// === abs(vector VectorNode) Vector ===
|
||||
func absImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
n := args[0].(VectorNode) |
||||
vector := n.Eval(timestamp) |
||||
for _, el := range vector { |
||||
el.Metric.Delete(clientmodel.MetricNameLabel) |
||||
el.Value = clientmodel.SampleValue(math.Abs(float64(el.Value))) |
||||
} |
||||
return vector |
||||
} |
||||
|
||||
// === absent(vector VectorNode) Vector ===
|
||||
func absentImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
n := args[0].(VectorNode) |
||||
if len(n.Eval(timestamp)) > 0 { |
||||
return Vector{} |
||||
} |
||||
m := clientmodel.Metric{} |
||||
if vs, ok := n.(*VectorSelector); ok { |
||||
for _, matcher := range vs.labelMatchers { |
||||
if matcher.Type == metric.Equal && matcher.Name != clientmodel.MetricNameLabel { |
||||
m[matcher.Name] = matcher.Value |
||||
} |
||||
} |
||||
} |
||||
return Vector{ |
||||
&Sample{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: m, |
||||
Copied: true, |
||||
}, |
||||
Value: 1, |
||||
Timestamp: timestamp, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
// === ceil(vector VectorNode) Vector ===
|
||||
func ceilImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
n := args[0].(VectorNode) |
||||
vector := n.Eval(timestamp) |
||||
for _, el := range vector { |
||||
el.Metric.Delete(clientmodel.MetricNameLabel) |
||||
el.Value = clientmodel.SampleValue(math.Ceil(float64(el.Value))) |
||||
} |
||||
return vector |
||||
} |
||||
|
||||
// === exp(vector VectorNode) Vector ===
|
||||
func expImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
n := args[0].(VectorNode) |
||||
vector := n.Eval(timestamp) |
||||
for _, el := range vector { |
||||
el.Metric.Delete(clientmodel.MetricNameLabel) |
||||
el.Value = clientmodel.SampleValue(math.Exp(float64(el.Value))) |
||||
} |
||||
return vector |
||||
} |
||||
|
||||
// === ln(vector VectorNode) Vector ===
|
||||
func lnImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
n := args[0].(VectorNode) |
||||
vector := n.Eval(timestamp) |
||||
for _, el := range vector { |
||||
el.Metric.Delete(clientmodel.MetricNameLabel) |
||||
el.Value = clientmodel.SampleValue(math.Log(float64(el.Value))) |
||||
} |
||||
return vector |
||||
} |
||||
|
||||
// === log2(vector VectorNode) Vector ===
|
||||
func log2Impl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
n := args[0].(VectorNode) |
||||
vector := n.Eval(timestamp) |
||||
for _, el := range vector { |
||||
el.Metric.Delete(clientmodel.MetricNameLabel) |
||||
el.Value = clientmodel.SampleValue(math.Log2(float64(el.Value))) |
||||
} |
||||
return vector |
||||
} |
||||
|
||||
// === log10(vector VectorNode) Vector ===
|
||||
func log10Impl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
n := args[0].(VectorNode) |
||||
vector := n.Eval(timestamp) |
||||
for _, el := range vector { |
||||
el.Metric.Delete(clientmodel.MetricNameLabel) |
||||
el.Value = clientmodel.SampleValue(math.Log10(float64(el.Value))) |
||||
} |
||||
return vector |
||||
} |
||||
|
||||
// === deriv(node MatrixNode) Vector ===
|
||||
func derivImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
matrixNode := args[0].(MatrixNode) |
||||
resultVector := Vector{} |
||||
|
||||
matrixValue := matrixNode.Eval(timestamp) |
||||
for _, samples := range matrixValue { |
||||
// No sense in trying to compute a derivative without at least two points.
|
||||
// Drop this vector element.
|
||||
if len(samples.Values) < 2 { |
||||
continue |
||||
} |
||||
|
||||
// Least squares.
|
||||
n := clientmodel.SampleValue(0) |
||||
sumY := clientmodel.SampleValue(0) |
||||
sumX := clientmodel.SampleValue(0) |
||||
sumXY := clientmodel.SampleValue(0) |
||||
sumX2 := clientmodel.SampleValue(0) |
||||
for _, sample := range samples.Values { |
||||
x := clientmodel.SampleValue(sample.Timestamp.UnixNano() / 1e9) |
||||
n += 1.0 |
||||
sumY += sample.Value |
||||
sumX += x |
||||
sumXY += x * sample.Value |
||||
sumX2 += x * x |
||||
} |
||||
numerator := sumXY - sumX*sumY/n |
||||
denominator := sumX2 - (sumX*sumX)/n |
||||
|
||||
resultValue := numerator / denominator |
||||
|
||||
resultSample := &Sample{ |
||||
Metric: samples.Metric, |
||||
Value: resultValue, |
||||
Timestamp: timestamp, |
||||
} |
||||
resultSample.Metric.Delete(clientmodel.MetricNameLabel) |
||||
resultVector = append(resultVector, resultSample) |
||||
} |
||||
return resultVector |
||||
} |
||||
|
||||
// === histogram_quantile(k ScalarNode, vector VectorNode) Vector ===
|
||||
func histogramQuantileImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { |
||||
q := args[0].(ScalarNode).Eval(timestamp) |
||||
inVec := args[1].(VectorNode).Eval(timestamp) |
||||
outVec := Vector{} |
||||
signatureToMetricWithBuckets := map[uint64]*metricWithBuckets{} |
||||
for _, el := range inVec { |
||||
upperBound, err := strconv.ParseFloat( |
||||
string(el.Metric.Metric[clientmodel.BucketLabel]), 64, |
||||
) |
||||
if err != nil { |
||||
// Oops, no bucket label or malformed label value. Skip.
|
||||
// TODO(beorn7): Issue a warning somehow.
|
||||
continue |
||||
} |
||||
signature := clientmodel.SignatureWithoutLabels(el.Metric.Metric, excludedLabels) |
||||
mb, ok := signatureToMetricWithBuckets[signature] |
||||
if !ok { |
||||
el.Metric.Delete(clientmodel.BucketLabel) |
||||
el.Metric.Delete(clientmodel.MetricNameLabel) |
||||
mb = &metricWithBuckets{el.Metric, nil} |
||||
signatureToMetricWithBuckets[signature] = mb |
||||
} |
||||
mb.buckets = append(mb.buckets, bucket{upperBound, el.Value}) |
||||
} |
||||
|
||||
for _, mb := range signatureToMetricWithBuckets { |
||||
outVec = append(outVec, &Sample{ |
||||
Metric: mb.metric, |
||||
Value: clientmodel.SampleValue(quantile(q, mb.buckets)), |
||||
Timestamp: timestamp, |
||||
}) |
||||
} |
||||
|
||||
return outVec |
||||
} |
||||
|
||||
var functions = map[string]*Function{ |
||||
"abs": { |
||||
name: "abs", |
||||
argTypes: []ExprType{VectorType}, |
||||
returnType: VectorType, |
||||
callFn: absImpl, |
||||
}, |
||||
"absent": { |
||||
name: "absent", |
||||
argTypes: []ExprType{VectorType}, |
||||
returnType: VectorType, |
||||
callFn: absentImpl, |
||||
}, |
||||
"avg_over_time": { |
||||
name: "avg_over_time", |
||||
argTypes: []ExprType{MatrixType}, |
||||
returnType: VectorType, |
||||
callFn: avgOverTimeImpl, |
||||
}, |
||||
"bottomk": { |
||||
name: "bottomk", |
||||
argTypes: []ExprType{ScalarType, VectorType}, |
||||
returnType: VectorType, |
||||
callFn: bottomkImpl, |
||||
}, |
||||
"ceil": { |
||||
name: "ceil", |
||||
argTypes: []ExprType{VectorType}, |
||||
returnType: VectorType, |
||||
callFn: ceilImpl, |
||||
}, |
||||
"count_over_time": { |
||||
name: "count_over_time", |
||||
argTypes: []ExprType{MatrixType}, |
||||
returnType: VectorType, |
||||
callFn: countOverTimeImpl, |
||||
}, |
||||
"count_scalar": { |
||||
name: "count_scalar", |
||||
argTypes: []ExprType{VectorType}, |
||||
returnType: ScalarType, |
||||
callFn: countScalarImpl, |
||||
}, |
||||
"delta": { |
||||
name: "delta", |
||||
argTypes: []ExprType{MatrixType, ScalarType}, |
||||
optionalArgs: 1, // The 2nd argument is deprecated.
|
||||
returnType: VectorType, |
||||
callFn: deltaImpl, |
||||
}, |
||||
"deriv": { |
||||
name: "deriv", |
||||
argTypes: []ExprType{MatrixType}, |
||||
returnType: VectorType, |
||||
callFn: derivImpl, |
||||
}, |
||||
"drop_common_labels": { |
||||
name: "drop_common_labels", |
||||
argTypes: []ExprType{VectorType}, |
||||
returnType: VectorType, |
||||
callFn: dropCommonLabelsImpl, |
||||
}, |
||||
"exp": { |
||||
name: "exp", |
||||
argTypes: []ExprType{VectorType}, |
||||
returnType: VectorType, |
||||
callFn: expImpl, |
||||
}, |
||||
"floor": { |
||||
name: "floor", |
||||
argTypes: []ExprType{VectorType}, |
||||
returnType: VectorType, |
||||
callFn: floorImpl, |
||||
}, |
||||
"histogram_quantile": { |
||||
name: "histogram_quantile", |
||||
argTypes: []ExprType{ScalarType, VectorType}, |
||||
returnType: VectorType, |
||||
callFn: histogramQuantileImpl, |
||||
}, |
||||
"ln": { |
||||
name: "ln", |
||||
argTypes: []ExprType{VectorType}, |
||||
returnType: VectorType, |
||||
callFn: lnImpl, |
||||
}, |
||||
"log10": { |
||||
name: "log10", |
||||
argTypes: []ExprType{VectorType}, |
||||
returnType: VectorType, |
||||
callFn: log10Impl, |
||||
}, |
||||
"log2": { |
||||
name: "log2", |
||||
argTypes: []ExprType{VectorType}, |
||||
returnType: VectorType, |
||||
callFn: log2Impl, |
||||
}, |
||||
"max_over_time": { |
||||
name: "max_over_time", |
||||
argTypes: []ExprType{MatrixType}, |
||||
returnType: VectorType, |
||||
callFn: maxOverTimeImpl, |
||||
}, |
||||
"min_over_time": { |
||||
name: "min_over_time", |
||||
argTypes: []ExprType{MatrixType}, |
||||
returnType: VectorType, |
||||
callFn: minOverTimeImpl, |
||||
}, |
||||
"rate": { |
||||
name: "rate", |
||||
argTypes: []ExprType{MatrixType}, |
||||
returnType: VectorType, |
||||
callFn: rateImpl, |
||||
}, |
||||
"round": { |
||||
name: "round", |
||||
argTypes: []ExprType{VectorType, ScalarType}, |
||||
optionalArgs: 1, |
||||
returnType: VectorType, |
||||
callFn: roundImpl, |
||||
}, |
||||
"scalar": { |
||||
name: "scalar", |
||||
argTypes: []ExprType{VectorType}, |
||||
returnType: ScalarType, |
||||
callFn: scalarImpl, |
||||
}, |
||||
"sort": { |
||||
name: "sort", |
||||
argTypes: []ExprType{VectorType}, |
||||
returnType: VectorType, |
||||
callFn: sortImpl, |
||||
}, |
||||
"sort_desc": { |
||||
name: "sort_desc", |
||||
argTypes: []ExprType{VectorType}, |
||||
returnType: VectorType, |
||||
callFn: sortDescImpl, |
||||
}, |
||||
"sum_over_time": { |
||||
name: "sum_over_time", |
||||
argTypes: []ExprType{MatrixType}, |
||||
returnType: VectorType, |
||||
callFn: sumOverTimeImpl, |
||||
}, |
||||
"time": { |
||||
name: "time", |
||||
argTypes: []ExprType{}, |
||||
returnType: ScalarType, |
||||
callFn: timeImpl, |
||||
}, |
||||
"topk": { |
||||
name: "topk", |
||||
argTypes: []ExprType{ScalarType, VectorType}, |
||||
returnType: VectorType, |
||||
callFn: topkImpl, |
||||
}, |
||||
} |
||||
|
||||
// GetFunction returns a predefined Function object for the given
|
||||
// name.
|
||||
func GetFunction(name string) (*Function, error) { |
||||
function, ok := functions[name] |
||||
if !ok { |
||||
return nil, fmt.Errorf("couldn't find function %v()", name) |
||||
} |
||||
return function, nil |
||||
} |
||||
@ -1,63 +0,0 @@ |
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ast |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
clientmodel "github.com/prometheus/client_golang/model" |
||||
|
||||
"github.com/prometheus/prometheus/storage/metric" |
||||
) |
||||
|
||||
type emptyRangeNode struct{} |
||||
|
||||
func (node emptyRangeNode) Type() ExprType { return MatrixType } |
||||
func (node emptyRangeNode) NodeTreeToDotGraph() string { return "" } |
||||
func (node emptyRangeNode) String() string { return "" } |
||||
func (node emptyRangeNode) Children() Nodes { return Nodes{} } |
||||
|
||||
func (node emptyRangeNode) Eval(timestamp clientmodel.Timestamp) Matrix { |
||||
return Matrix{ |
||||
SampleStream{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{clientmodel.MetricNameLabel: "empty_metric"}, |
||||
}, |
||||
Values: metric.Values{}, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func (node emptyRangeNode) EvalBoundaries(timestamp clientmodel.Timestamp) Matrix { |
||||
return Matrix{ |
||||
SampleStream{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{clientmodel.MetricNameLabel: "empty_metric"}, |
||||
}, |
||||
Values: metric.Values{}, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func TestDeltaWithEmptyElementDoesNotCrash(t *testing.T) { |
||||
now := clientmodel.Now() |
||||
vector := deltaImpl(now, []Node{emptyRangeNode{}, &ScalarLiteral{value: 0}}).(Vector) |
||||
if len(vector) != 0 { |
||||
t.Fatalf("Expected empty result vector, got: %v", vector) |
||||
} |
||||
vector = deltaImpl(now, []Node{emptyRangeNode{}, &ScalarLiteral{value: 1}}).(Vector) |
||||
if len(vector) != 0 { |
||||
t.Fatalf("Expected empty result vector, got: %v", vector) |
||||
} |
||||
} |
||||
@ -1,441 +0,0 @@ |
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ast |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
"fmt" |
||||
"reflect" |
||||
"sort" |
||||
"strings" |
||||
|
||||
clientmodel "github.com/prometheus/client_golang/model" |
||||
|
||||
"github.com/prometheus/prometheus/stats" |
||||
"github.com/prometheus/prometheus/storage/local" |
||||
"github.com/prometheus/prometheus/utility" |
||||
) |
||||
|
||||
// OutputFormat is an enum for the possible output formats.
|
||||
type OutputFormat int |
||||
|
||||
// Possible output formats.
|
||||
const ( |
||||
Text OutputFormat = iota |
||||
JSON |
||||
) |
||||
|
||||
const jsonFormatVersion = 1 |
||||
|
||||
func (opType BinOpType) String() string { |
||||
opTypeMap := map[BinOpType]string{ |
||||
Add: "+", |
||||
Sub: "-", |
||||
Mul: "*", |
||||
Div: "/", |
||||
Mod: "%", |
||||
GT: ">", |
||||
LT: "<", |
||||
EQ: "==", |
||||
NE: "!=", |
||||
GE: ">=", |
||||
LE: "<=", |
||||
And: "AND", |
||||
Or: "OR", |
||||
} |
||||
return opTypeMap[opType] |
||||
} |
||||
|
||||
func (aggrType AggrType) String() string { |
||||
aggrTypeMap := map[AggrType]string{ |
||||
Sum: "SUM", |
||||
Avg: "AVG", |
||||
Min: "MIN", |
||||
Max: "MAX", |
||||
Count: "COUNT", |
||||
} |
||||
return aggrTypeMap[aggrType] |
||||
} |
||||
|
||||
func (exprType ExprType) String() string { |
||||
exprTypeMap := map[ExprType]string{ |
||||
ScalarType: "scalar", |
||||
VectorType: "vector", |
||||
MatrixType: "matrix", |
||||
StringType: "string", |
||||
} |
||||
return exprTypeMap[exprType] |
||||
} |
||||
|
||||
func (vector Vector) String() string { |
||||
metricStrings := make([]string, 0, len(vector)) |
||||
for _, sample := range vector { |
||||
metricStrings = append(metricStrings, |
||||
fmt.Sprintf("%s => %v @[%v]", |
||||
sample.Metric, |
||||
sample.Value, sample.Timestamp)) |
||||
} |
||||
return strings.Join(metricStrings, "\n") |
||||
} |
||||
|
||||
func (matrix Matrix) String() string { |
||||
metricStrings := make([]string, 0, len(matrix)) |
||||
for _, sampleStream := range matrix { |
||||
metricName, hasName := sampleStream.Metric.Metric[clientmodel.MetricNameLabel] |
||||
numLabels := len(sampleStream.Metric.Metric) |
||||
if hasName { |
||||
numLabels-- |
||||
} |
||||
labelStrings := make([]string, 0, numLabels) |
||||
for label, value := range sampleStream.Metric.Metric { |
||||
if label != clientmodel.MetricNameLabel { |
||||
labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value)) |
||||
} |
||||
} |
||||
sort.Strings(labelStrings) |
||||
valueStrings := make([]string, 0, len(sampleStream.Values)) |
||||
for _, value := range sampleStream.Values { |
||||
valueStrings = append(valueStrings, |
||||
fmt.Sprintf("\n%v @[%v]", value.Value, value.Timestamp)) |
||||
} |
||||
metricStrings = append(metricStrings, |
||||
fmt.Sprintf("%s{%s} => %s", |
||||
metricName, |
||||
strings.Join(labelStrings, ", "), |
||||
strings.Join(valueStrings, ", "))) |
||||
} |
||||
sort.Strings(metricStrings) |
||||
return strings.Join(metricStrings, "\n") |
||||
} |
||||
|
||||
// ErrorToJSON converts the given error into JSON.
|
||||
func ErrorToJSON(err error) string { |
||||
errorStruct := struct { |
||||
Type string `json:"type"` |
||||
Value string `json:"value"` |
||||
Version int `json:"version"` |
||||
}{ |
||||
Type: "error", |
||||
Value: err.Error(), |
||||
Version: jsonFormatVersion, |
||||
} |
||||
|
||||
errorJSON, err := json.Marshal(errorStruct) |
||||
if err != nil { |
||||
return "" |
||||
} |
||||
return string(errorJSON) |
||||
} |
||||
|
||||
// TypedValueToJSON converts the given data of type 'scalar',
|
||||
// 'vector', or 'matrix' into its JSON representation.
|
||||
func TypedValueToJSON(data interface{}, typeStr string) string { |
||||
dataStruct := struct { |
||||
Type string `json:"type"` |
||||
Value interface{} `json:"value"` |
||||
Version int `json:"version"` |
||||
}{ |
||||
Type: typeStr, |
||||
Value: data, |
||||
Version: jsonFormatVersion, |
||||
} |
||||
dataJSON, err := json.Marshal(dataStruct) |
||||
if err != nil { |
||||
return ErrorToJSON(err) |
||||
} |
||||
return string(dataJSON) |
||||
} |
||||
|
||||
// EvalToString evaluates the given node into a string of the given format.
|
||||
func EvalToString(node Node, timestamp clientmodel.Timestamp, format OutputFormat, storage local.Storage, queryStats *stats.TimerGroup) string { |
||||
totalEvalTimer := queryStats.GetTimer(stats.TotalEvalTime).Start() |
||||
defer totalEvalTimer.Stop() |
||||
|
||||
prepareTimer := queryStats.GetTimer(stats.TotalQueryPreparationTime).Start() |
||||
closer, err := prepareInstantQuery(node, timestamp, storage, queryStats) |
||||
prepareTimer.Stop() |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
defer closer.Close() |
||||
|
||||
evalTimer := queryStats.GetTimer(stats.InnerEvalTime).Start() |
||||
switch node.Type() { |
||||
case ScalarType: |
||||
scalar := node.(ScalarNode).Eval(timestamp) |
||||
evalTimer.Stop() |
||||
switch format { |
||||
case Text: |
||||
return fmt.Sprintf("scalar: %v @[%v]", scalar, timestamp) |
||||
case JSON: |
||||
return TypedValueToJSON(scalar, "scalar") |
||||
} |
||||
case VectorType: |
||||
vector := node.(VectorNode).Eval(timestamp) |
||||
evalTimer.Stop() |
||||
switch format { |
||||
case Text: |
||||
return vector.String() |
||||
case JSON: |
||||
return TypedValueToJSON(vector, "vector") |
||||
} |
||||
case MatrixType: |
||||
matrix := node.(MatrixNode).Eval(timestamp) |
||||
evalTimer.Stop() |
||||
switch format { |
||||
case Text: |
||||
return matrix.String() |
||||
case JSON: |
||||
return TypedValueToJSON(matrix, "matrix") |
||||
} |
||||
case StringType: |
||||
str := node.(StringNode).Eval(timestamp) |
||||
evalTimer.Stop() |
||||
switch format { |
||||
case Text: |
||||
return str |
||||
case JSON: |
||||
return TypedValueToJSON(str, "string") |
||||
} |
||||
} |
||||
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 local.Storage, queryStats *stats.TimerGroup) (Vector, error) { |
||||
totalEvalTimer := queryStats.GetTimer(stats.TotalEvalTime).Start() |
||||
defer totalEvalTimer.Stop() |
||||
|
||||
prepareTimer := queryStats.GetTimer(stats.TotalQueryPreparationTime).Start() |
||||
closer, err := prepareInstantQuery(node, timestamp, storage, queryStats) |
||||
prepareTimer.Stop() |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
defer closer.Close() |
||||
|
||||
evalTimer := queryStats.GetTimer(stats.InnerEvalTime).Start() |
||||
switch node.Type() { |
||||
case ScalarType: |
||||
scalar := node.(ScalarNode).Eval(timestamp) |
||||
evalTimer.Stop() |
||||
return Vector{&Sample{Value: scalar}}, nil |
||||
case VectorType: |
||||
vector := node.(VectorNode).Eval(timestamp) |
||||
evalTimer.Stop() |
||||
return vector, nil |
||||
case MatrixType: |
||||
return nil, errors.New("matrices not supported by EvalToVector") |
||||
case StringType: |
||||
str := node.(StringNode).Eval(timestamp) |
||||
evalTimer.Stop() |
||||
return Vector{ |
||||
&Sample{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
"__value__": clientmodel.LabelValue(str), |
||||
}, |
||||
Copied: true, |
||||
}, |
||||
}, |
||||
}, nil |
||||
} |
||||
panic("Switch didn't cover all node types") |
||||
} |
||||
|
||||
// NodeTreeToDotGraph returns a DOT representation of the scalar
|
||||
// literal.
|
||||
func (node *ScalarLiteral) NodeTreeToDotGraph() string { |
||||
return fmt.Sprintf("%#p[label=\"%v\"];\n", node, node.value) |
||||
} |
||||
|
||||
func functionArgsToDotGraph(node Node, args []Node) string { |
||||
graph := "" |
||||
for _, arg := range args { |
||||
graph += fmt.Sprintf("%x -> %x;\n", reflect.ValueOf(node).Pointer(), reflect.ValueOf(arg).Pointer()) |
||||
} |
||||
for _, arg := range args { |
||||
graph += arg.NodeTreeToDotGraph() |
||||
} |
||||
return graph |
||||
} |
||||
|
||||
// NodeTreeToDotGraph returns a DOT representation of the function
|
||||
// call.
|
||||
func (node *ScalarFunctionCall) NodeTreeToDotGraph() string { |
||||
graph := fmt.Sprintf("%#p[label=\"%s\"];\n", node, node.function.name) |
||||
graph += functionArgsToDotGraph(node, node.args) |
||||
return graph |
||||
} |
||||
|
||||
// NodeTreeToDotGraph returns a DOT representation of the expression.
|
||||
func (node *ScalarArithExpr) NodeTreeToDotGraph() string { |
||||
nodeAddr := reflect.ValueOf(node).Pointer() |
||||
graph := fmt.Sprintf( |
||||
` |
||||
%x[label="%s"]; |
||||
%x -> %x; |
||||
%x -> %x; |
||||
%s |
||||
%s |
||||
}`, |
||||
nodeAddr, node.opType, |
||||
nodeAddr, reflect.ValueOf(node.lhs).Pointer(), |
||||
nodeAddr, reflect.ValueOf(node.rhs).Pointer(), |
||||
node.lhs.NodeTreeToDotGraph(), |
||||
node.rhs.NodeTreeToDotGraph(), |
||||
) |
||||
return graph |
||||
} |
||||
|
||||
// NodeTreeToDotGraph returns a DOT representation of the vector selector.
|
||||
func (node *VectorSelector) NodeTreeToDotGraph() string { |
||||
return fmt.Sprintf("%#p[label=\"%s\"];\n", node, node) |
||||
} |
||||
|
||||
// NodeTreeToDotGraph returns a DOT representation of the function
|
||||
// call.
|
||||
func (node *VectorFunctionCall) NodeTreeToDotGraph() string { |
||||
graph := fmt.Sprintf("%#p[label=\"%s\"];\n", node, node.function.name) |
||||
graph += functionArgsToDotGraph(node, node.args) |
||||
return graph |
||||
} |
||||
|
||||
// NodeTreeToDotGraph returns a DOT representation of the vector
|
||||
// aggregation.
|
||||
func (node *VectorAggregation) NodeTreeToDotGraph() string { |
||||
groupByStrings := make([]string, 0, len(node.groupBy)) |
||||
for _, label := range node.groupBy { |
||||
groupByStrings = append(groupByStrings, string(label)) |
||||
} |
||||
|
||||
graph := fmt.Sprintf("%#p[label=\"%s BY (%s)\"]\n", |
||||
node, |
||||
node.aggrType, |
||||
strings.Join(groupByStrings, ", ")) |
||||
graph += fmt.Sprintf("%#p -> %x;\n", node, reflect.ValueOf(node.vector).Pointer()) |
||||
graph += node.vector.NodeTreeToDotGraph() |
||||
return graph |
||||
} |
||||
|
||||
// NodeTreeToDotGraph returns a DOT representation of the expression.
|
||||
func (node *VectorArithExpr) NodeTreeToDotGraph() string { |
||||
nodeAddr := reflect.ValueOf(node).Pointer() |
||||
graph := fmt.Sprintf( |
||||
` |
||||
%x[label="%s"]; |
||||
%x -> %x; |
||||
%x -> %x; |
||||
%s |
||||
%s |
||||
}`, |
||||
nodeAddr, node.opType, |
||||
nodeAddr, reflect.ValueOf(node.lhs).Pointer(), |
||||
nodeAddr, reflect.ValueOf(node.rhs).Pointer(), |
||||
node.lhs.NodeTreeToDotGraph(), |
||||
node.rhs.NodeTreeToDotGraph(), |
||||
) |
||||
return graph |
||||
} |
||||
|
||||
// NodeTreeToDotGraph returns a DOT representation of the matrix
|
||||
// selector.
|
||||
func (node *MatrixSelector) NodeTreeToDotGraph() string { |
||||
return fmt.Sprintf("%#p[label=\"%s\"];\n", node, node) |
||||
} |
||||
|
||||
// NodeTreeToDotGraph returns a DOT representation of the string
|
||||
// literal.
|
||||
func (node *StringLiteral) NodeTreeToDotGraph() string { |
||||
return fmt.Sprintf("%#p[label=\"'%q'\"];\n", node, node.str) |
||||
} |
||||
|
||||
// NodeTreeToDotGraph returns a DOT representation of the function
|
||||
// call.
|
||||
func (node *StringFunctionCall) NodeTreeToDotGraph() string { |
||||
graph := fmt.Sprintf("%#p[label=\"%s\"];\n", node, node.function.name) |
||||
graph += functionArgsToDotGraph(node, node.args) |
||||
return graph |
||||
} |
||||
|
||||
func (nodes Nodes) String() string { |
||||
nodeStrings := make([]string, 0, len(nodes)) |
||||
for _, node := range nodes { |
||||
nodeStrings = append(nodeStrings, node.String()) |
||||
} |
||||
return strings.Join(nodeStrings, ", ") |
||||
} |
||||
|
||||
func (node *ScalarLiteral) String() string { |
||||
return fmt.Sprint(node.value) |
||||
} |
||||
|
||||
func (node *ScalarFunctionCall) String() string { |
||||
return fmt.Sprintf("%s(%s)", node.function.name, node.args) |
||||
} |
||||
|
||||
func (node *ScalarArithExpr) String() string { |
||||
return fmt.Sprintf("(%s %s %s)", node.lhs, node.opType, node.rhs) |
||||
} |
||||
|
||||
func (node *VectorSelector) String() string { |
||||
labelStrings := make([]string, 0, len(node.labelMatchers)-1) |
||||
var metricName clientmodel.LabelValue |
||||
for _, matcher := range node.labelMatchers { |
||||
if matcher.Name != clientmodel.MetricNameLabel { |
||||
labelStrings = append(labelStrings, fmt.Sprintf("%s%s%q", matcher.Name, matcher.Type, matcher.Value)) |
||||
} else { |
||||
metricName = matcher.Value |
||||
} |
||||
} |
||||
|
||||
switch len(labelStrings) { |
||||
case 0: |
||||
return string(metricName) |
||||
default: |
||||
sort.Strings(labelStrings) |
||||
return fmt.Sprintf("%s{%s}", metricName, strings.Join(labelStrings, ",")) |
||||
} |
||||
} |
||||
|
||||
func (node *VectorFunctionCall) String() string { |
||||
return fmt.Sprintf("%s(%s)", node.function.name, node.args) |
||||
} |
||||
|
||||
func (node *VectorAggregation) String() string { |
||||
aggrString := fmt.Sprintf("%s(%s)", node.aggrType, node.vector) |
||||
if len(node.groupBy) > 0 { |
||||
return fmt.Sprintf("%s BY (%s)", aggrString, node.groupBy) |
||||
} |
||||
return aggrString |
||||
} |
||||
|
||||
func (node *VectorArithExpr) String() string { |
||||
return fmt.Sprintf("(%s %s %s)", node.lhs, node.opType, node.rhs) |
||||
} |
||||
|
||||
func (node *MatrixSelector) String() string { |
||||
vectorString := (&VectorSelector{labelMatchers: node.labelMatchers}).String() |
||||
intervalString := fmt.Sprintf("[%s]", utility.DurationToString(node.interval)) |
||||
return vectorString + intervalString |
||||
} |
||||
|
||||
func (node *StringLiteral) String() string { |
||||
return fmt.Sprintf("%q", node.str) |
||||
} |
||||
|
||||
func (node *StringFunctionCall) String() string { |
||||
return fmt.Sprintf("%s(%s)", node.function.name, node.args) |
||||
} |
||||
@ -1,106 +0,0 @@ |
||||
// Copyright 2015 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ast |
||||
|
||||
import ( |
||||
"math" |
||||
"sort" |
||||
|
||||
clientmodel "github.com/prometheus/client_golang/model" |
||||
) |
||||
|
||||
// Helpers to calculate quantiles.
|
||||
|
||||
// excludedLabels are the labels to exclude from signature calculation for
|
||||
// quantiles.
|
||||
var excludedLabels = map[clientmodel.LabelName]struct{}{ |
||||
clientmodel.MetricNameLabel: {}, |
||||
clientmodel.BucketLabel: {}, |
||||
} |
||||
|
||||
type bucket struct { |
||||
upperBound float64 |
||||
count clientmodel.SampleValue |
||||
} |
||||
|
||||
// buckets implements sort.Interface.
|
||||
type buckets []bucket |
||||
|
||||
func (b buckets) Len() int { return len(b) } |
||||
func (b buckets) Swap(i, j int) { b[i], b[j] = b[j], b[i] } |
||||
func (b buckets) Less(i, j int) bool { return b[i].upperBound < b[j].upperBound } |
||||
|
||||
type metricWithBuckets struct { |
||||
metric clientmodel.COWMetric |
||||
buckets buckets |
||||
} |
||||
|
||||
// quantile calculates the quantile 'q' based on the given buckets. The buckets
|
||||
// will be sorted by upperBound by this function (i.e. no sorting needed before
|
||||
// calling this function). The quantile value is interpolated assuming a linear
|
||||
// distribution within a bucket. However, if the quantile falls into the highest
|
||||
// bucket, the upper bound of the 2nd highest bucket is returned. A natural
|
||||
// lower bound of 0 is assumed if the upper bound of the lowest bucket is
|
||||
// greater 0. In that case, interpolation in the lowest bucket happens linearly
|
||||
// between 0 and the upper bound of the lowest bucket. However, if the lowest
|
||||
// bucket has an upper bound less or equal 0, this upper bound is returned if
|
||||
// the quantile falls into the lowest bucket.
|
||||
//
|
||||
// There are a number of special cases (once we have a way to report errors
|
||||
// happening during evaluations of AST functions, we should report those
|
||||
// explicitly):
|
||||
//
|
||||
// If 'buckets' has fewer than 2 elements, NaN is returned.
|
||||
//
|
||||
// If the highest bucket is not +Inf, NaN is returned.
|
||||
//
|
||||
// If q<0, -Inf is returned.
|
||||
//
|
||||
// If q>1, +Inf is returned.
|
||||
func quantile(q clientmodel.SampleValue, buckets buckets) float64 { |
||||
if q < 0 { |
||||
return math.Inf(-1) |
||||
} |
||||
if q > 1 { |
||||
return math.Inf(+1) |
||||
} |
||||
if len(buckets) < 2 { |
||||
return math.NaN() |
||||
} |
||||
sort.Sort(buckets) |
||||
if !math.IsInf(buckets[len(buckets)-1].upperBound, +1) { |
||||
return math.NaN() |
||||
} |
||||
|
||||
rank := q * buckets[len(buckets)-1].count |
||||
b := sort.Search(len(buckets)-1, func(i int) bool { return buckets[i].count >= rank }) |
||||
|
||||
if b == len(buckets)-1 { |
||||
return buckets[len(buckets)-2].upperBound |
||||
} |
||||
if b == 0 && buckets[0].upperBound <= 0 { |
||||
return buckets[0].upperBound |
||||
} |
||||
var ( |
||||
bucketStart float64 |
||||
bucketEnd = buckets[b].upperBound |
||||
count = buckets[b].count |
||||
) |
||||
if b > 0 { |
||||
bucketStart = buckets[b-1].upperBound |
||||
count -= buckets[b-1].count |
||||
rank -= buckets[b-1].count |
||||
} |
||||
return bucketStart + (bucketEnd-bucketStart)*float64(rank/count) |
||||
} |
||||
@ -1,229 +0,0 @@ |
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ast |
||||
|
||||
import ( |
||||
"time" |
||||
|
||||
clientmodel "github.com/prometheus/client_golang/model" |
||||
|
||||
"github.com/prometheus/prometheus/stats" |
||||
"github.com/prometheus/prometheus/storage/local" |
||||
) |
||||
|
||||
// preloadTimes tracks which instants or ranges to preload for a set of
|
||||
// fingerprints. One of these structs is collected for each offset by the query
|
||||
// analyzer.
|
||||
type preloadTimes struct { |
||||
// Instants require single samples to be loaded along the entire query
|
||||
// range, with intervals between the samples corresponding to the query
|
||||
// resolution.
|
||||
instants map[clientmodel.Fingerprint]struct{} |
||||
// Ranges require loading a range of samples at each resolution step,
|
||||
// stretching backwards from the current evaluation timestamp. The length of
|
||||
// the range into the past is given by the duration, as in "foo[5m]".
|
||||
ranges map[clientmodel.Fingerprint]time.Duration |
||||
} |
||||
|
||||
// A queryAnalyzer recursively traverses the AST to look for any nodes
|
||||
// which will need data from the datastore. Instantiate with
|
||||
// newQueryAnalyzer.
|
||||
type queryAnalyzer struct { |
||||
// Tracks one set of times to preload per offset that occurs in the query
|
||||
// expression.
|
||||
offsetPreloadTimes map[time.Duration]preloadTimes |
||||
// The underlying storage to which the query will be applied. Needed for
|
||||
// extracting timeseries fingerprint information during query analysis.
|
||||
storage local.Storage |
||||
} |
||||
|
||||
// newQueryAnalyzer returns a pointer to a newly instantiated
|
||||
// queryAnalyzer. The storage is needed to extract timeseries
|
||||
// fingerprint information during query analysis.
|
||||
func newQueryAnalyzer(storage local.Storage) *queryAnalyzer { |
||||
return &queryAnalyzer{ |
||||
offsetPreloadTimes: map[time.Duration]preloadTimes{}, |
||||
storage: storage, |
||||
} |
||||
} |
||||
|
||||
func (analyzer *queryAnalyzer) getPreloadTimes(offset time.Duration) preloadTimes { |
||||
if _, ok := analyzer.offsetPreloadTimes[offset]; !ok { |
||||
analyzer.offsetPreloadTimes[offset] = preloadTimes{ |
||||
instants: map[clientmodel.Fingerprint]struct{}{}, |
||||
ranges: map[clientmodel.Fingerprint]time.Duration{}, |
||||
} |
||||
} |
||||
return analyzer.offsetPreloadTimes[offset] |
||||
} |
||||
|
||||
// visit implements the visitor interface.
|
||||
func (analyzer *queryAnalyzer) visit(node Node) { |
||||
switch n := node.(type) { |
||||
case *VectorSelector: |
||||
pt := analyzer.getPreloadTimes(n.offset) |
||||
fingerprints := analyzer.storage.GetFingerprintsForLabelMatchers(n.labelMatchers) |
||||
n.fingerprints = fingerprints |
||||
for _, fp := range fingerprints { |
||||
// Only add the fingerprint to the instants if not yet present in the
|
||||
// ranges. Ranges always contain more points and span more time than
|
||||
// instants for the same offset.
|
||||
if _, alreadyInRanges := pt.ranges[fp]; !alreadyInRanges { |
||||
pt.instants[fp] = struct{}{} |
||||
} |
||||
|
||||
n.metrics[fp] = analyzer.storage.GetMetricForFingerprint(fp) |
||||
} |
||||
case *MatrixSelector: |
||||
pt := analyzer.getPreloadTimes(n.offset) |
||||
fingerprints := analyzer.storage.GetFingerprintsForLabelMatchers(n.labelMatchers) |
||||
n.fingerprints = fingerprints |
||||
for _, fp := range fingerprints { |
||||
if pt.ranges[fp] < n.interval { |
||||
pt.ranges[fp] = n.interval |
||||
// Delete the fingerprint from the instants. Ranges always contain more
|
||||
// points and span more time than instants, so we don't need to track
|
||||
// an instant for the same fingerprint, should we have one.
|
||||
delete(pt.instants, fp) |
||||
} |
||||
|
||||
n.metrics[fp] = analyzer.storage.GetMetricForFingerprint(fp) |
||||
} |
||||
} |
||||
} |
||||
|
||||
type iteratorInitializer struct { |
||||
storage local.Storage |
||||
} |
||||
|
||||
func (i *iteratorInitializer) visit(node Node) { |
||||
switch n := node.(type) { |
||||
case *VectorSelector: |
||||
for _, fp := range n.fingerprints { |
||||
n.iterators[fp] = i.storage.NewIterator(fp) |
||||
} |
||||
case *MatrixSelector: |
||||
for _, fp := range n.fingerprints { |
||||
n.iterators[fp] = i.storage.NewIterator(fp) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func prepareInstantQuery(node Node, timestamp clientmodel.Timestamp, storage local.Storage, queryStats *stats.TimerGroup) (local.Preloader, error) { |
||||
totalTimer := queryStats.GetTimer(stats.TotalEvalTime) |
||||
|
||||
analyzeTimer := queryStats.GetTimer(stats.QueryAnalysisTime).Start() |
||||
analyzer := newQueryAnalyzer(storage) |
||||
Walk(analyzer, node) |
||||
analyzeTimer.Stop() |
||||
|
||||
preloadTimer := queryStats.GetTimer(stats.PreloadTime).Start() |
||||
p := storage.NewPreloader() |
||||
for offset, pt := range analyzer.offsetPreloadTimes { |
||||
ts := timestamp.Add(-offset) |
||||
for fp, rangeDuration := range pt.ranges { |
||||
if et := totalTimer.ElapsedTime(); et > *queryTimeout { |
||||
preloadTimer.Stop() |
||||
p.Close() |
||||
return nil, queryTimeoutError{et} |
||||
} |
||||
if err := p.PreloadRange(fp, ts.Add(-rangeDuration), ts, *stalenessDelta); err != nil { |
||||
preloadTimer.Stop() |
||||
p.Close() |
||||
return nil, err |
||||
} |
||||
} |
||||
for fp := range pt.instants { |
||||
if et := totalTimer.ElapsedTime(); et > *queryTimeout { |
||||
preloadTimer.Stop() |
||||
p.Close() |
||||
return nil, queryTimeoutError{et} |
||||
} |
||||
if err := p.PreloadRange(fp, ts, ts, *stalenessDelta); err != nil { |
||||
preloadTimer.Stop() |
||||
p.Close() |
||||
return nil, err |
||||
} |
||||
} |
||||
} |
||||
preloadTimer.Stop() |
||||
|
||||
ii := &iteratorInitializer{ |
||||
storage: storage, |
||||
} |
||||
Walk(ii, node) |
||||
|
||||
return p, nil |
||||
} |
||||
|
||||
func prepareRangeQuery(node Node, start clientmodel.Timestamp, end clientmodel.Timestamp, interval time.Duration, storage local.Storage, queryStats *stats.TimerGroup) (local.Preloader, error) { |
||||
totalTimer := queryStats.GetTimer(stats.TotalEvalTime) |
||||
|
||||
analyzeTimer := queryStats.GetTimer(stats.QueryAnalysisTime).Start() |
||||
analyzer := newQueryAnalyzer(storage) |
||||
Walk(analyzer, node) |
||||
analyzeTimer.Stop() |
||||
|
||||
preloadTimer := queryStats.GetTimer(stats.PreloadTime).Start() |
||||
p := storage.NewPreloader() |
||||
for offset, pt := range analyzer.offsetPreloadTimes { |
||||
offsetStart := start.Add(-offset) |
||||
offsetEnd := end.Add(-offset) |
||||
for fp, rangeDuration := range pt.ranges { |
||||
if et := totalTimer.ElapsedTime(); et > *queryTimeout { |
||||
preloadTimer.Stop() |
||||
p.Close() |
||||
return nil, queryTimeoutError{et} |
||||
} |
||||
if err := p.PreloadRange(fp, offsetStart.Add(-rangeDuration), offsetEnd, *stalenessDelta); err != nil { |
||||
preloadTimer.Stop() |
||||
p.Close() |
||||
return nil, err |
||||
} |
||||
/* |
||||
if interval < rangeDuration { |
||||
if err := p.GetMetricRange(fp, offsetEnd, offsetEnd.Sub(offsetStart)+rangeDuration); err != nil { |
||||
p.Close() |
||||
return nil, err |
||||
} |
||||
} else { |
||||
if err := p.GetMetricRangeAtInterval(fp, offsetStart, offsetEnd, interval, rangeDuration); err != nil { |
||||
p.Close() |
||||
return nil, err |
||||
} |
||||
} |
||||
*/ |
||||
} |
||||
for fp := range pt.instants { |
||||
if et := totalTimer.ElapsedTime(); et > *queryTimeout { |
||||
preloadTimer.Stop() |
||||
p.Close() |
||||
return nil, queryTimeoutError{et} |
||||
} |
||||
if err := p.PreloadRange(fp, offsetStart, offsetEnd, *stalenessDelta); err != nil { |
||||
preloadTimer.Stop() |
||||
p.Close() |
||||
return nil, err |
||||
} |
||||
} |
||||
} |
||||
preloadTimer.Stop() |
||||
|
||||
ii := &iteratorInitializer{ |
||||
storage: storage, |
||||
} |
||||
Walk(ii, node) |
||||
|
||||
return p, nil |
||||
} |
||||
@ -1,28 +0,0 @@ |
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ast |
||||
|
||||
// visitor is the interface for a Node visitor.
|
||||
type visitor interface { |
||||
visit(node Node) |
||||
} |
||||
|
||||
// Walk does a depth-first traversal of the AST, starting at node,
|
||||
// calling visitor.visit for each encountered Node in the tree.
|
||||
func Walk(v visitor, node Node) { |
||||
v.visit(node) |
||||
for _, childNode := range node.Children() { |
||||
Walk(v, childNode) |
||||
} |
||||
} |
||||
@ -1,16 +0,0 @@ |
||||
// A simple test recording rule. |
||||
dc_http_request_rate5m = sum(rate(http_request_count[5m])) by (dc) |
||||
|
||||
// A simple test alerting rule. |
||||
ALERT GlobalRequestRateLow IF(dc_http_request_rate5m < 10000) FOR 5m WITH { |
||||
service = "testservice" |
||||
/* ... more fields here ... */ |
||||
} |
||||
SUMMARY "Global request rate low" |
||||
DESCRIPTION "The global request rate is low" |
||||
|
||||
foo = bar{label1="value1"} |
||||
|
||||
ALERT BazAlert IF(foo > 10) WITH {} |
||||
SUMMARY "Baz" |
||||
DESCRIPTION "BazAlert" |
||||
@ -1 +0,0 @@ |
||||
now = time() |
||||
@ -1,15 +0,0 @@ |
||||
// A simple test recording rule. |
||||
dc_http_request_rate5m = sum(rate(http_request_count[5m])) by (dc) |
||||
|
||||
// A simple test alerting rule with a syntax error (invalid duration string "5"). |
||||
ALERT GlobalRequestRateLow IF(dc_http_request_rate5m < 10000) FOR 5 WITH { |
||||
description = "Global HTTP request rate low!", |
||||
summary = "Request rate low" |
||||
/* ... more fields here ... */ |
||||
} |
||||
SUMMARY "summary" |
||||
DESCRIPTION "description" |
||||
|
||||
foo = bar{label1="value1"} |
||||
|
||||
ALERT BazAlert IF(foo > 10) WITH {} SUMMARY "summary" DESCRIPTION "description" |
||||
@ -1,221 +0,0 @@ |
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rules |
||||
|
||||
import ( |
||||
"fmt" |
||||
"net/url" |
||||
"strings" |
||||
|
||||
clientmodel "github.com/prometheus/client_golang/model" |
||||
|
||||
"github.com/prometheus/prometheus/rules/ast" |
||||
"github.com/prometheus/prometheus/storage/metric" |
||||
"github.com/prometheus/prometheus/utility" |
||||
) |
||||
|
||||
// CreateRecordingRule is a convenience function to create a recording rule.
|
||||
func CreateRecordingRule(name string, labels clientmodel.LabelSet, expr ast.Node, permanent bool) (*RecordingRule, error) { |
||||
if _, ok := expr.(ast.VectorNode); !ok { |
||||
return nil, fmt.Errorf("recording rule expression %v does not evaluate to vector type", expr) |
||||
} |
||||
return &RecordingRule{ |
||||
name: name, |
||||
labels: labels, |
||||
vector: expr.(ast.VectorNode), |
||||
permanent: permanent, |
||||
}, nil |
||||
} |
||||
|
||||
// CreateAlertingRule is a convenience function to create a new alerting rule.
|
||||
func CreateAlertingRule(name string, expr ast.Node, holdDurationStr string, labels clientmodel.LabelSet, summary string, description string) (*AlertingRule, error) { |
||||
if _, ok := expr.(ast.VectorNode); !ok { |
||||
return nil, fmt.Errorf("alert rule expression %v does not evaluate to vector type", expr) |
||||
} |
||||
holdDuration, err := utility.StringToDuration(holdDurationStr) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return NewAlertingRule(name, expr.(ast.VectorNode), holdDuration, labels, summary, description), nil |
||||
} |
||||
|
||||
// NewScalarLiteral returns a ScalarLiteral with the given value. If sign is "-"
|
||||
// the value is negated.
|
||||
func NewScalarLiteral(value clientmodel.SampleValue, sign string) *ast.ScalarLiteral { |
||||
if sign == "-" { |
||||
value = -value |
||||
} |
||||
return ast.NewScalarLiteral(value) |
||||
} |
||||
|
||||
// NewFunctionCall is a convenience function to create a new AST function-call node.
|
||||
func NewFunctionCall(name string, args []ast.Node) (ast.Node, error) { |
||||
function, err := ast.GetFunction(name) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("unknown function %q", name) |
||||
} |
||||
functionCall, err := ast.NewFunctionCall(function, args) |
||||
if err != nil { |
||||
return nil, fmt.Errorf(err.Error()) |
||||
} |
||||
return functionCall, nil |
||||
} |
||||
|
||||
// NewVectorAggregation is a convenience function to create a new AST vector aggregation.
|
||||
func NewVectorAggregation(aggrTypeStr string, vector ast.Node, groupBy clientmodel.LabelNames, keepExtraLabels bool) (*ast.VectorAggregation, error) { |
||||
if _, ok := vector.(ast.VectorNode); !ok { |
||||
return nil, fmt.Errorf("operand of %v aggregation must be of vector type", aggrTypeStr) |
||||
} |
||||
var aggrTypes = map[string]ast.AggrType{ |
||||
"SUM": ast.Sum, |
||||
"MAX": ast.Max, |
||||
"MIN": ast.Min, |
||||
"AVG": ast.Avg, |
||||
"COUNT": ast.Count, |
||||
} |
||||
aggrType, ok := aggrTypes[aggrTypeStr] |
||||
if !ok { |
||||
return nil, fmt.Errorf("unknown aggregation type %q", aggrTypeStr) |
||||
} |
||||
return ast.NewVectorAggregation(aggrType, vector.(ast.VectorNode), groupBy, keepExtraLabels), nil |
||||
} |
||||
|
||||
// vectorMatching combines data used to match samples between vectors.
|
||||
type vectorMatching struct { |
||||
matchCardinality ast.VectorMatchCardinality |
||||
matchOn clientmodel.LabelNames |
||||
includeLabels clientmodel.LabelNames |
||||
} |
||||
|
||||
// newVectorMatching is a convenience function to create a new vectorMatching.
|
||||
func newVectorMatching(card string, matchOn, include clientmodel.LabelNames) (*vectorMatching, error) { |
||||
var matchCardinalities = map[string]ast.VectorMatchCardinality{ |
||||
"": ast.MatchOneToOne, |
||||
"GROUP_LEFT": ast.MatchManyToOne, |
||||
"GROUP_RIGHT": ast.MatchOneToMany, |
||||
} |
||||
matchCard, ok := matchCardinalities[card] |
||||
if !ok { |
||||
return nil, fmt.Errorf("invalid vector match cardinality %q", card) |
||||
} |
||||
if matchCard != ast.MatchOneToOne && len(include) == 0 { |
||||
return nil, fmt.Errorf("grouped vector matching must provide labels") |
||||
} |
||||
// There must be no overlap between both labelname lists.
|
||||
for _, matchLabel := range matchOn { |
||||
for _, incLabel := range include { |
||||
if matchLabel == incLabel { |
||||
return nil, fmt.Errorf("use of label %s in ON and %s clauses not allowed", incLabel, card) |
||||
} |
||||
} |
||||
} |
||||
return &vectorMatching{matchCard, matchOn, include}, nil |
||||
} |
||||
|
||||
// NewArithExpr is a convenience function to create a new AST arithmetic expression.
|
||||
func NewArithExpr(opTypeStr string, lhs ast.Node, rhs ast.Node, vecMatching *vectorMatching) (ast.Node, error) { |
||||
var opTypes = map[string]ast.BinOpType{ |
||||
"+": ast.Add, |
||||
"-": ast.Sub, |
||||
"*": ast.Mul, |
||||
"/": ast.Div, |
||||
"%": ast.Mod, |
||||
">": ast.GT, |
||||
"<": ast.LT, |
||||
"==": ast.EQ, |
||||
"!=": ast.NE, |
||||
">=": ast.GE, |
||||
"<=": ast.LE, |
||||
"AND": ast.And, |
||||
"OR": ast.Or, |
||||
} |
||||
opType, ok := opTypes[opTypeStr] |
||||
if !ok { |
||||
return nil, fmt.Errorf("invalid binary operator %q", opTypeStr) |
||||
} |
||||
var vm vectorMatching |
||||
if vecMatching != nil { |
||||
vm = *vecMatching |
||||
// And/or always do many-to-many matching.
|
||||
if opType == ast.And || opType == ast.Or { |
||||
vm.matchCardinality = ast.MatchManyToMany |
||||
} |
||||
} |
||||
expr, err := ast.NewArithExpr(opType, lhs, rhs, vm.matchCardinality, vm.matchOn, vm.includeLabels) |
||||
if err != nil { |
||||
return nil, fmt.Errorf(err.Error()) |
||||
} |
||||
return expr, nil |
||||
} |
||||
|
||||
// NewVectorSelector is a convenience function to create a new AST vector selector.
|
||||
func NewVectorSelector(m metric.LabelMatchers, offsetStr string) (ast.VectorNode, error) { |
||||
offset, err := utility.StringToDuration(offsetStr) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return ast.NewVectorSelector(m, offset), nil |
||||
} |
||||
|
||||
// NewMatrixSelector is a convenience function to create a new AST matrix selector.
|
||||
func NewMatrixSelector(vector ast.Node, intervalStr string, offsetStr string) (ast.MatrixNode, error) { |
||||
interval, err := utility.StringToDuration(intervalStr) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
offset, err := utility.StringToDuration(offsetStr) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
vectorSelector, ok := vector.(*ast.VectorSelector) |
||||
if !ok { |
||||
return nil, fmt.Errorf("intervals are currently only supported for vector selectors") |
||||
} |
||||
return ast.NewMatrixSelector(vectorSelector, interval, offset), nil |
||||
} |
||||
|
||||
func newLabelMatcher(matchTypeStr string, name clientmodel.LabelName, value clientmodel.LabelValue) (*metric.LabelMatcher, error) { |
||||
matchTypes := map[string]metric.MatchType{ |
||||
"=": metric.Equal, |
||||
"!=": metric.NotEqual, |
||||
"=~": metric.RegexMatch, |
||||
"!~": metric.RegexNoMatch, |
||||
} |
||||
matchType, ok := matchTypes[matchTypeStr] |
||||
if !ok { |
||||
return nil, fmt.Errorf("invalid label matching operator %q", matchTypeStr) |
||||
} |
||||
return metric.NewLabelMatcher(matchType, name, value) |
||||
} |
||||
|
||||
// TableLinkForExpression creates an escaped relative link to the table view of
|
||||
// the provided expression.
|
||||
func TableLinkForExpression(expr string) string { |
||||
// 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)) |
||||
} |
||||
|
||||
// GraphLinkForExpression creates an escaped relative link to the graph view of
|
||||
// the provided expression.
|
||||
func GraphLinkForExpression(expr string) string { |
||||
urlData := url.QueryEscape(fmt.Sprintf(`[{"expr":%q,"tab":0}]`, expr)) |
||||
return fmt.Sprintf("/graph#%s", strings.Replace(urlData, "+", "%20", -1)) |
||||
} |
||||
@ -1,487 +0,0 @@ |
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rules |
||||
|
||||
import ( |
||||
"time" |
||||
|
||||
clientmodel "github.com/prometheus/client_golang/model" |
||||
|
||||
"github.com/prometheus/prometheus/rules/ast" |
||||
"github.com/prometheus/prometheus/storage/local" |
||||
"github.com/prometheus/prometheus/storage/metric" |
||||
) |
||||
|
||||
var testSampleInterval = time.Duration(5) * time.Minute |
||||
var testStartTime = clientmodel.Timestamp(0) |
||||
|
||||
func getTestValueStream(startVal clientmodel.SampleValue, endVal clientmodel.SampleValue, stepVal clientmodel.SampleValue, startTime clientmodel.Timestamp) (resultValues metric.Values) { |
||||
currentTime := startTime |
||||
for currentVal := startVal; currentVal <= endVal; currentVal += stepVal { |
||||
sample := metric.SamplePair{ |
||||
Value: currentVal, |
||||
Timestamp: currentTime, |
||||
} |
||||
resultValues = append(resultValues, sample) |
||||
currentTime = currentTime.Add(testSampleInterval) |
||||
} |
||||
return resultValues |
||||
} |
||||
|
||||
func getTestVectorFromTestMatrix(matrix ast.Matrix) ast.Vector { |
||||
vector := ast.Vector{} |
||||
for _, sampleStream := range matrix { |
||||
lastSample := sampleStream.Values[len(sampleStream.Values)-1] |
||||
vector = append(vector, &ast.Sample{ |
||||
Metric: sampleStream.Metric, |
||||
Value: lastSample.Value, |
||||
Timestamp: lastSample.Timestamp, |
||||
}) |
||||
} |
||||
return vector |
||||
} |
||||
|
||||
func storeMatrix(storage local.Storage, matrix ast.Matrix) { |
||||
pendingSamples := clientmodel.Samples{} |
||||
for _, sampleStream := range matrix { |
||||
for _, sample := range sampleStream.Values { |
||||
pendingSamples = append(pendingSamples, &clientmodel.Sample{ |
||||
Metric: sampleStream.Metric.Metric, |
||||
Value: sample.Value, |
||||
Timestamp: sample.Timestamp, |
||||
}) |
||||
} |
||||
} |
||||
for _, s := range pendingSamples { |
||||
storage.Append(s) |
||||
} |
||||
storage.WaitForIndexing() |
||||
} |
||||
|
||||
var testMatrix = ast.Matrix{ |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "http_requests", |
||||
clientmodel.JobLabel: "api-server", |
||||
"instance": "0", |
||||
"group": "production", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 100, 10, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "http_requests", |
||||
clientmodel.JobLabel: "api-server", |
||||
"instance": "1", |
||||
"group": "production", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 200, 20, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "http_requests", |
||||
clientmodel.JobLabel: "api-server", |
||||
"instance": "0", |
||||
"group": "canary", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 300, 30, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "http_requests", |
||||
clientmodel.JobLabel: "api-server", |
||||
"instance": "1", |
||||
"group": "canary", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 400, 40, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "http_requests", |
||||
clientmodel.JobLabel: "app-server", |
||||
"instance": "0", |
||||
"group": "production", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 500, 50, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "http_requests", |
||||
clientmodel.JobLabel: "app-server", |
||||
"instance": "1", |
||||
"group": "production", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 600, 60, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "http_requests", |
||||
clientmodel.JobLabel: "app-server", |
||||
"instance": "0", |
||||
"group": "canary", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 700, 70, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "http_requests", |
||||
clientmodel.JobLabel: "app-server", |
||||
"instance": "1", |
||||
"group": "canary", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 800, 80, testStartTime), |
||||
}, |
||||
// Single-letter metric and label names.
|
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "x", |
||||
"y": "testvalue", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 100, 10, testStartTime), |
||||
}, |
||||
// Counter reset in the middle of range.
|
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "testcounter_reset_middle", |
||||
}, |
||||
}, |
||||
Values: append(getTestValueStream(0, 40, 10, testStartTime), getTestValueStream(0, 50, 10, testStartTime.Add(testSampleInterval*5))...), |
||||
}, |
||||
// Counter reset at the end of range.
|
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "testcounter_reset_end", |
||||
}, |
||||
}, |
||||
Values: append(getTestValueStream(0, 90, 10, testStartTime), getTestValueStream(0, 0, 10, testStartTime.Add(testSampleInterval*10))...), |
||||
}, |
||||
// For label-key grouping regression test.
|
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "label_grouping_test", |
||||
"a": "aa", |
||||
"b": "bb", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 100, 10, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "label_grouping_test", |
||||
"a": "a", |
||||
"b": "abb", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 200, 20, testStartTime), |
||||
}, |
||||
// Two histograms with 4 buckets each (*_sum and *_count not included,
|
||||
// only buckets). Lowest bucket for one histogram < 0, for the other >
|
||||
// 0. They have the same name, just separated by label. Not useful in
|
||||
// practice, but can happen (if clients change bucketing), and the
|
||||
// server has to cope with it.
|
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "testhistogram_bucket", |
||||
"le": "0.1", |
||||
"start": "positive", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 50, 5, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "testhistogram_bucket", |
||||
"le": ".2", |
||||
"start": "positive", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 70, 7, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "testhistogram_bucket", |
||||
"le": "1e0", |
||||
"start": "positive", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 110, 11, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "testhistogram_bucket", |
||||
"le": "+Inf", |
||||
"start": "positive", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 120, 12, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "testhistogram_bucket", |
||||
"le": "-.2", |
||||
"start": "negative", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 10, 1, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "testhistogram_bucket", |
||||
"le": "-0.1", |
||||
"start": "negative", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 20, 2, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "testhistogram_bucket", |
||||
"le": "0.3", |
||||
"start": "negative", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 20, 2, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "testhistogram_bucket", |
||||
"le": "+Inf", |
||||
"start": "negative", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 30, 3, testStartTime), |
||||
}, |
||||
// Now a more realistic histogram per job and instance to test aggregation.
|
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "request_duration_seconds_bucket", |
||||
clientmodel.JobLabel: "job1", |
||||
"instance": "ins1", |
||||
"le": "0.1", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 10, 1, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "request_duration_seconds_bucket", |
||||
clientmodel.JobLabel: "job1", |
||||
"instance": "ins1", |
||||
"le": "0.2", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 30, 3, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "request_duration_seconds_bucket", |
||||
clientmodel.JobLabel: "job1", |
||||
"instance": "ins1", |
||||
"le": "+Inf", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 40, 4, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "request_duration_seconds_bucket", |
||||
clientmodel.JobLabel: "job1", |
||||
"instance": "ins2", |
||||
"le": "0.1", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 20, 2, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "request_duration_seconds_bucket", |
||||
clientmodel.JobLabel: "job1", |
||||
"instance": "ins2", |
||||
"le": "0.2", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 50, 5, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "request_duration_seconds_bucket", |
||||
clientmodel.JobLabel: "job1", |
||||
"instance": "ins2", |
||||
"le": "+Inf", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 60, 6, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "request_duration_seconds_bucket", |
||||
clientmodel.JobLabel: "job2", |
||||
"instance": "ins1", |
||||
"le": "0.1", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 30, 3, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "request_duration_seconds_bucket", |
||||
clientmodel.JobLabel: "job2", |
||||
"instance": "ins1", |
||||
"le": "0.2", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 40, 4, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "request_duration_seconds_bucket", |
||||
clientmodel.JobLabel: "job2", |
||||
"instance": "ins1", |
||||
"le": "+Inf", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 60, 6, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "request_duration_seconds_bucket", |
||||
clientmodel.JobLabel: "job2", |
||||
"instance": "ins2", |
||||
"le": "0.1", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 40, 4, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "request_duration_seconds_bucket", |
||||
clientmodel.JobLabel: "job2", |
||||
"instance": "ins2", |
||||
"le": "0.2", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 70, 7, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "request_duration_seconds_bucket", |
||||
clientmodel.JobLabel: "job2", |
||||
"instance": "ins2", |
||||
"le": "+Inf", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 90, 9, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "vector_matching_a", |
||||
"l": "x", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 100, 1, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "vector_matching_a", |
||||
"l": "y", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 100, 2, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "vector_matching_b", |
||||
"l": "x", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 100, 4, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "cpu_count", |
||||
"instance": "0", |
||||
"type": "numa", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 500, 30, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "cpu_count", |
||||
"instance": "0", |
||||
"type": "smp", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 200, 10, testStartTime), |
||||
}, |
||||
{ |
||||
Metric: clientmodel.COWMetric{ |
||||
Metric: clientmodel.Metric{ |
||||
clientmodel.MetricNameLabel: "cpu_count", |
||||
"instance": "1", |
||||
"type": "smp", |
||||
}, |
||||
}, |
||||
Values: getTestValueStream(0, 200, 20, testStartTime), |
||||
}, |
||||
} |
||||
|
||||
var testVector = getTestVectorFromTestMatrix(testMatrix) |
||||
@ -1,118 +0,0 @@ |
||||
/* Copyright 2013 The Prometheus Authors |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. */ |
||||
|
||||
%{ |
||||
package rules |
||||
|
||||
import ( |
||||
"fmt" |
||||
"strconv" |
||||
"strings" |
||||
|
||||
clientmodel "github.com/prometheus/client_golang/model" |
||||
) |
||||
|
||||
// Lex is called by the parser generated by "go tool yacc" to obtain each |
||||
// token. The method is opened before the matching rules block and closed at |
||||
// the end of the file. |
||||
func (lexer *RulesLexer) Lex(lval *yySymType) int { |
||||
// Internal lexer states. |
||||
const ( |
||||
S_INITIAL = iota |
||||
S_COMMENTS |
||||
) |
||||
|
||||
// We simulate multiple start symbols for closely-related grammars via dummy tokens. See |
||||
// http://www.gnu.org/software/bison/manual/html_node/Multiple-start_002dsymbols.html |
||||
// Reason: we want to be able to parse lists of named rules as well as single expressions. |
||||
if lexer.startToken != 0 { |
||||
startToken := lexer.startToken |
||||
lexer.startToken = 0 |
||||
return startToken |
||||
} |
||||
|
||||
c := lexer.current |
||||
currentState := 0 |
||||
|
||||
if lexer.empty { |
||||
c, lexer.empty = lexer.getChar(), false |
||||
} |
||||
|
||||
%} |
||||
|
||||
D [0-9] |
||||
L [a-zA-Z_] |
||||
M [a-zA-Z_:] |
||||
U [smhdwy] |
||||
|
||||
FLOAT ({D}*\.?{D}+|{D}+\.?{D}*){EXPONENT}?|[+-]?[iI][nN][fF]|[nN][aA][nN] |
||||
EXPONENT [eE][-+]?[0-9]+ |
||||
|
||||
STR \"(\\.|[^\\"])*\"|\'(\\.|[^\\'])*\' |
||||
|
||||
%x S_COMMENTS |
||||
|
||||
%yyc c |
||||
%yyn c = lexer.getChar() |
||||
%yyt currentState |
||||
|
||||
%% |
||||
lexer.buf = lexer.buf[:0] // The code before the first rule executed before every scan cycle (rule #0 / state 0 action) |
||||
|
||||
"/*" currentState = S_COMMENTS |
||||
<S_COMMENTS>"*/" currentState = S_INITIAL |
||||
<S_COMMENTS>.|\n /* ignore chars within multi-line comments */ |
||||
|
||||
\/\/[^\r\n]*\n /* gobble up one-line comments */ |
||||
|
||||
ALERT|alert return ALERT |
||||
IF|if return IF |
||||
FOR|for return FOR |
||||
WITH|with return WITH |
||||
SUMMARY|summary return SUMMARY |
||||
DESCRIPTION|description return DESCRIPTION |
||||
|
||||
PERMANENT|permanent return PERMANENT |
||||
BY|by return GROUP_OP |
||||
ON|on return MATCH_OP |
||||
GROUP_LEFT|GROUP_RIGHT lval.str = lexer.token(); return MATCH_MOD |
||||
group_left|group_right lval.str = strings.ToUpper(lexer.token()); return MATCH_MOD |
||||
KEEPING_EXTRA|keeping_extra return KEEPING_EXTRA |
||||
OFFSET|offset return OFFSET |
||||
AVG|SUM|MAX|MIN|COUNT lval.str = lexer.token(); return AGGR_OP |
||||
avg|sum|max|min|count lval.str = strings.ToUpper(lexer.token()); return AGGR_OP |
||||
\<|>|AND|OR|and|or lval.str = strings.ToUpper(lexer.token()); return CMP_OP |
||||
==|!=|>=|<=|=~|!~ lval.str = lexer.token(); return CMP_OP |
||||
[+\-] lval.str = lexer.token(); return ADDITIVE_OP |
||||
[*/%] lval.str = lexer.token(); return MULT_OP |
||||
|
||||
{FLOAT} num, err := strconv.ParseFloat(lexer.token(), 64); |
||||
if (err != nil && err.(*strconv.NumError).Err == strconv.ErrSyntax) { |
||||
panic("Invalid float") |
||||
} |
||||
lval.num = clientmodel.SampleValue(num) |
||||
return NUMBER |
||||
|
||||
{D}+{U} lval.str = lexer.token(); return DURATION |
||||
{L}({L}|{D})* lval.str = lexer.token(); return IDENTIFIER |
||||
{M}({M}|{D})* lval.str = lexer.token(); return METRICNAME |
||||
|
||||
{STR} lval.str = lexer.token()[1:len(lexer.token()) - 1]; return STRING |
||||
|
||||
[{}\[\]()=,] return int(lexer.buf[0]) |
||||
[\t\n\r ] /* gobble up any whitespace */ |
||||
%% |
||||
|
||||
lexer.empty = true |
||||
return int(c) |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -1,164 +0,0 @@ |
||||
// Copyright 2013 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rules |
||||
|
||||
import ( |
||||
"bufio" |
||||
"errors" |
||||
"fmt" |
||||
"io" |
||||
"os" |
||||
"strings" |
||||
|
||||
"github.com/golang/glog" |
||||
|
||||
"github.com/prometheus/prometheus/rules/ast" |
||||
) |
||||
|
||||
// RulesLexer is the lexer for rule expressions.
|
||||
type RulesLexer struct { |
||||
// Errors encountered during parsing.
|
||||
errors []string |
||||
// Dummy token to simulate multiple start symbols (see below).
|
||||
startToken int |
||||
// Parsed full rules.
|
||||
parsedRules []Rule |
||||
// Parsed single expression.
|
||||
parsedExpr ast.Node |
||||
|
||||
// Current character.
|
||||
current byte |
||||
// Current token buffer.
|
||||
buf []byte |
||||
// Input text.
|
||||
src *bufio.Reader |
||||
// Whether we have a current char.
|
||||
empty bool |
||||
|
||||
// Current input line.
|
||||
line int |
||||
// Current character position within the current input line.
|
||||
pos int |
||||
} |
||||
|
||||
func (lexer *RulesLexer) Error(errorStr string) { |
||||
err := fmt.Sprintf("Error parsing rules at line %v, char %v: %v", lexer.line, lexer.pos, errorStr) |
||||
lexer.errors = append(lexer.errors, err) |
||||
} |
||||
|
||||
func (lexer *RulesLexer) getChar() byte { |
||||
if lexer.current != 0 { |
||||
lexer.buf = append(lexer.buf, lexer.current) |
||||
} |
||||
lexer.current = 0 |
||||
if b, err := lexer.src.ReadByte(); err == nil { |
||||
if b == '\n' { |
||||
lexer.line++ |
||||
lexer.pos = 0 |
||||
} else { |
||||
lexer.pos++ |
||||
} |
||||
lexer.current = b |
||||
} else if err != io.EOF { |
||||
glog.Fatal(err) |
||||
} |
||||
return lexer.current |
||||
} |
||||
|
||||
func (lexer *RulesLexer) token() string { |
||||
return string(lexer.buf) |
||||
} |
||||
|
||||
func newRulesLexer(src io.Reader, singleExpr bool) *RulesLexer { |
||||
lexer := &RulesLexer{ |
||||
startToken: START_RULES, |
||||
src: bufio.NewReader(src), |
||||
pos: 1, |
||||
line: 1, |
||||
} |
||||
|
||||
if singleExpr { |
||||
lexer.startToken = START_EXPRESSION |
||||
} |
||||
lexer.getChar() |
||||
return lexer |
||||
} |
||||
|
||||
func lexAndParse(rulesReader io.Reader, singleExpr bool) (*RulesLexer, error) { |
||||
lexer := newRulesLexer(rulesReader, singleExpr) |
||||
ret := yyParse(lexer) |
||||
if ret != 0 && len(lexer.errors) == 0 { |
||||
lexer.Error("unknown parser error") |
||||
} |
||||
|
||||
if len(lexer.errors) > 0 { |
||||
err := errors.New(strings.Join(lexer.errors, "\n")) |
||||
return nil, err |
||||
} |
||||
return lexer, nil |
||||
} |
||||
|
||||
// LoadRulesFromReader parses rules from the provided reader and returns them.
|
||||
func LoadRulesFromReader(rulesReader io.Reader) ([]Rule, error) { |
||||
lexer, err := lexAndParse(rulesReader, false) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return lexer.parsedRules, err |
||||
} |
||||
|
||||
// LoadRulesFromString parses rules from the provided string returns them.
|
||||
func LoadRulesFromString(rulesString string) ([]Rule, error) { |
||||
rulesReader := strings.NewReader(rulesString) |
||||
return LoadRulesFromReader(rulesReader) |
||||
} |
||||
|
||||
// LoadRulesFromFile parses rules from the file of the provided name and returns
|
||||
// them.
|
||||
func LoadRulesFromFile(fileName string) ([]Rule, error) { |
||||
rulesReader, err := os.Open(fileName) |
||||
if err != nil { |
||||
return []Rule{}, err |
||||
} |
||||
defer rulesReader.Close() |
||||
return LoadRulesFromReader(rulesReader) |
||||
} |
||||
|
||||
// LoadExprFromReader parses a single expression from the provided reader and
|
||||
// returns it as an AST node.
|
||||
func LoadExprFromReader(exprReader io.Reader) (ast.Node, error) { |
||||
lexer, err := lexAndParse(exprReader, true) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return lexer.parsedExpr, err |
||||
} |
||||
|
||||
// LoadExprFromString parses a single expression from the provided string and
|
||||
// returns it as an AST node.
|
||||
func LoadExprFromString(exprString string) (ast.Node, error) { |
||||
exprReader := strings.NewReader(exprString) |
||||
return LoadExprFromReader(exprReader) |
||||
} |
||||
|
||||
// LoadExprFromFile parses a single expression from the file of the provided
|
||||
// name and returns it as an AST node.
|
||||
func LoadExprFromFile(fileName string) (ast.Node, error) { |
||||
exprReader, err := os.Open(fileName) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer exprReader.Close() |
||||
return LoadExprFromReader(exprReader) |
||||
} |
||||
@ -1,281 +0,0 @@ |
||||
// Copyright 2013 The Prometheus Authors |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
%{ |
||||
package rules |
||||
|
||||
import ( |
||||
clientmodel "github.com/prometheus/client_golang/model" |
||||
|
||||
"github.com/prometheus/prometheus/rules/ast" |
||||
"github.com/prometheus/prometheus/storage/metric" |
||||
) |
||||
%} |
||||
|
||||
%union { |
||||
num clientmodel.SampleValue |
||||
str string |
||||
ruleNode ast.Node |
||||
ruleNodeSlice []ast.Node |
||||
boolean bool |
||||
labelNameSlice clientmodel.LabelNames |
||||
labelSet clientmodel.LabelSet |
||||
labelMatcher *metric.LabelMatcher |
||||
labelMatchers metric.LabelMatchers |
||||
vectorMatching *vectorMatching |
||||
} |
||||
|
||||
/* We simulate multiple start symbols for closely-related grammars via dummy tokens. See |
||||
http://www.gnu.org/software/bison/manual/html_node/Multiple-start_002dsymbols.html |
||||
Reason: we want to be able to parse lists of named rules as well as single expressions. |
||||
*/ |
||||
%token START_RULES START_EXPRESSION |
||||
|
||||
%token <str> IDENTIFIER STRING DURATION METRICNAME |
||||
%token <num> NUMBER |
||||
%token PERMANENT GROUP_OP KEEPING_EXTRA OFFSET MATCH_OP |
||||
%token <str> AGGR_OP CMP_OP ADDITIVE_OP MULT_OP MATCH_MOD |
||||
%token ALERT IF FOR WITH SUMMARY DESCRIPTION |
||||
|
||||
%type <ruleNodeSlice> func_arg_list |
||||
%type <labelNameSlice> label_list grouping_opts |
||||
%type <labelSet> label_assign label_assign_list rule_labels |
||||
%type <labelMatcher> label_match |
||||
%type <labelMatchers> label_match_list label_matches |
||||
%type <vectorMatching> vector_matching |
||||
%type <ruleNode> rule_expr func_arg |
||||
%type <boolean> qualifier extra_labels_opts |
||||
%type <str> for_duration metric_name label_match_type offset_opts |
||||
|
||||
%right '=' |
||||
%left CMP_OP |
||||
%left ADDITIVE_OP |
||||
%left MULT_OP |
||||
%start start |
||||
|
||||
%% |
||||
start : START_RULES rules_stat_list |
||||
| START_EXPRESSION saved_rule_expr |
||||
; |
||||
|
||||
rules_stat_list : /* empty */ |
||||
| rules_stat_list rules_stat |
||||
; |
||||
|
||||
saved_rule_expr : rule_expr |
||||
{ yylex.(*RulesLexer).parsedExpr = $1 } |
||||
; |
||||
|
||||
|
||||
rules_stat : qualifier metric_name rule_labels '=' rule_expr |
||||
{ |
||||
rule, err := CreateRecordingRule($2, $3, $5, $1) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule) |
||||
} |
||||
| ALERT IDENTIFIER IF rule_expr for_duration WITH rule_labels SUMMARY STRING DESCRIPTION STRING |
||||
{ |
||||
rule, err := CreateAlertingRule($2, $4, $5, $7, $9, $11) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule) |
||||
} |
||||
; |
||||
|
||||
for_duration : /* empty */ |
||||
{ $$ = "0s" } |
||||
| FOR DURATION |
||||
{ $$ = $2 } |
||||
; |
||||
|
||||
qualifier : /* empty */ |
||||
{ $$ = false } |
||||
| PERMANENT |
||||
{ $$ = true } |
||||
; |
||||
|
||||
metric_name : METRICNAME |
||||
{ $$ = $1 } |
||||
| IDENTIFIER |
||||
{ $$ = $1 } |
||||
; |
||||
|
||||
rule_labels : /* empty */ |
||||
{ $$ = clientmodel.LabelSet{} } |
||||
| '{' label_assign_list '}' |
||||
{ $$ = $2 } |
||||
| '{' '}' |
||||
{ $$ = clientmodel.LabelSet{} } |
||||
|
||||
label_assign_list : label_assign |
||||
{ $$ = $1 } |
||||
| label_assign_list ',' label_assign |
||||
{ for k, v := range $3 { $$[k] = v } } |
||||
; |
||||
|
||||
label_assign : IDENTIFIER '=' STRING |
||||
{ $$ = clientmodel.LabelSet{ clientmodel.LabelName($1): clientmodel.LabelValue($3) } } |
||||
; |
||||
|
||||
label_matches : /* empty */ |
||||
{ $$ = metric.LabelMatchers{} } |
||||
| '{' '}' |
||||
{ $$ = metric.LabelMatchers{} } |
||||
| '{' label_match_list '}' |
||||
{ $$ = $2 } |
||||
; |
||||
|
||||
label_match_list : label_match |
||||
{ $$ = metric.LabelMatchers{$1} } |
||||
| label_match_list ',' label_match |
||||
{ $$ = append($$, $3) } |
||||
; |
||||
|
||||
label_match : IDENTIFIER label_match_type STRING |
||||
{ |
||||
var err error |
||||
$$, err = newLabelMatcher($2, clientmodel.LabelName($1), clientmodel.LabelValue($3)) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
} |
||||
; |
||||
|
||||
label_match_type : '=' |
||||
{ $$ = "=" } |
||||
| CMP_OP |
||||
{ $$ = $1 } |
||||
; |
||||
|
||||
offset_opts : /* empty */ |
||||
{ $$ = "0s" } |
||||
| OFFSET DURATION |
||||
{ $$ = $2 } |
||||
; |
||||
|
||||
rule_expr : '(' rule_expr ')' |
||||
{ $$ = $2 } |
||||
| '{' label_match_list '}' offset_opts |
||||
{ |
||||
var err error |
||||
$$, err = NewVectorSelector($2, $4) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
} |
||||
| metric_name label_matches offset_opts |
||||
{ |
||||
var err error |
||||
m, err := metric.NewLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, clientmodel.LabelValue($1)) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
$2 = append($2, m) |
||||
$$, err = NewVectorSelector($2, $3) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
} |
||||
| IDENTIFIER '(' func_arg_list ')' |
||||
{ |
||||
var err error |
||||
$$, err = NewFunctionCall($1, $3) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
} |
||||
| IDENTIFIER '(' ')' |
||||
{ |
||||
var err error |
||||
$$, err = NewFunctionCall($1, []ast.Node{}) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
} |
||||
| rule_expr '[' DURATION ']' offset_opts |
||||
{ |
||||
var err error |
||||
$$, err = NewMatrixSelector($1, $3, $5) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
} |
||||
| AGGR_OP '(' rule_expr ')' grouping_opts extra_labels_opts |
||||
{ |
||||
var err error |
||||
$$, err = NewVectorAggregation($1, $3, $5, $6) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
} |
||||
| AGGR_OP grouping_opts extra_labels_opts '(' rule_expr ')' |
||||
{ |
||||
var err error |
||||
$$, err = NewVectorAggregation($1, $5, $2, $3) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
} |
||||
/* Yacc can only attach associativity to terminals, so we |
||||
* have to list all operators here. */ |
||||
| rule_expr ADDITIVE_OP vector_matching rule_expr |
||||
{ |
||||
var err error |
||||
$$, err = NewArithExpr($2, $1, $4, $3) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
} |
||||
| rule_expr MULT_OP vector_matching rule_expr |
||||
{ |
||||
var err error |
||||
$$, err = NewArithExpr($2, $1, $4, $3) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
} |
||||
| rule_expr CMP_OP vector_matching rule_expr |
||||
{ |
||||
var err error |
||||
$$, err = NewArithExpr($2, $1, $4, $3) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
} |
||||
| NUMBER |
||||
{ $$ = NewScalarLiteral($1, "+")} |
||||
| ADDITIVE_OP NUMBER |
||||
{ $$ = NewScalarLiteral($2, $1)} |
||||
; |
||||
|
||||
extra_labels_opts : /* empty */ |
||||
{ $$ = false } |
||||
| KEEPING_EXTRA |
||||
{ $$ = true } |
||||
; |
||||
|
||||
vector_matching : /* empty */ |
||||
{ $$ = nil } |
||||
| MATCH_OP '(' label_list ')' |
||||
{ |
||||
var err error |
||||
$$, err = newVectorMatching("", $3, nil) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
} |
||||
| MATCH_OP '(' label_list ')' MATCH_MOD '(' label_list ')' |
||||
{ |
||||
var err error |
||||
$$, err = newVectorMatching($5, $3, $7) |
||||
if err != nil { yylex.Error(err.Error()); return 1 } |
||||
} |
||||
; |
||||
|
||||
grouping_opts : |
||||
{ $$ = clientmodel.LabelNames{} } |
||||
| GROUP_OP '(' label_list ')' |
||||
{ $$ = $3 } |
||||
; |
||||
|
||||
label_list : IDENTIFIER |
||||
{ $$ = clientmodel.LabelNames{clientmodel.LabelName($1)} } |
||||
| label_list ',' IDENTIFIER |
||||
{ $$ = append($$, clientmodel.LabelName($3)) } |
||||
; |
||||
|
||||
func_arg_list : func_arg |
||||
{ $$ = []ast.Node{$1} } |
||||
| func_arg_list ',' func_arg |
||||
{ $$ = append($$, $3) } |
||||
; |
||||
|
||||
func_arg : rule_expr |
||||
{ $$ = $1 } |
||||
| STRING |
||||
{ $$ = ast.NewStringLiteral($1) } |
||||
; |
||||
%% |
||||
@ -1,784 +0,0 @@ |
||||
//line parser.y:15
|
||||
package rules |
||||
|
||||
import __yyfmt__ "fmt" |
||||
|
||||
//line parser.y:15
|
||||
import ( |
||||
clientmodel "github.com/prometheus/client_golang/model" |
||||
|
||||
"github.com/prometheus/prometheus/rules/ast" |
||||
"github.com/prometheus/prometheus/storage/metric" |
||||
) |
||||
|
||||
//line parser.y:25
|
||||
type yySymType struct { |
||||
yys int |
||||
num clientmodel.SampleValue |
||||
str string |
||||
ruleNode ast.Node |
||||
ruleNodeSlice []ast.Node |
||||
boolean bool |
||||
labelNameSlice clientmodel.LabelNames |
||||
labelSet clientmodel.LabelSet |
||||
labelMatcher *metric.LabelMatcher |
||||
labelMatchers metric.LabelMatchers |
||||
vectorMatching *vectorMatching |
||||
} |
||||
|
||||
const START_RULES = 57346 |
||||
const START_EXPRESSION = 57347 |
||||
const IDENTIFIER = 57348 |
||||
const STRING = 57349 |
||||
const DURATION = 57350 |
||||
const METRICNAME = 57351 |
||||
const NUMBER = 57352 |
||||
const PERMANENT = 57353 |
||||
const GROUP_OP = 57354 |
||||
const KEEPING_EXTRA = 57355 |
||||
const OFFSET = 57356 |
||||
const MATCH_OP = 57357 |
||||
const AGGR_OP = 57358 |
||||
const CMP_OP = 57359 |
||||
const ADDITIVE_OP = 57360 |
||||
const MULT_OP = 57361 |
||||
const MATCH_MOD = 57362 |
||||
const ALERT = 57363 |
||||
const IF = 57364 |
||||
const FOR = 57365 |
||||
const WITH = 57366 |
||||
const SUMMARY = 57367 |
||||
const DESCRIPTION = 57368 |
||||
|
||||
var yyToknames = []string{ |
||||
"START_RULES", |
||||
"START_EXPRESSION", |
||||
"IDENTIFIER", |
||||
"STRING", |
||||
"DURATION", |
||||
"METRICNAME", |
||||
"NUMBER", |
||||
"PERMANENT", |
||||
"GROUP_OP", |
||||
"KEEPING_EXTRA", |
||||
"OFFSET", |
||||
"MATCH_OP", |
||||
"AGGR_OP", |
||||
"CMP_OP", |
||||
"ADDITIVE_OP", |
||||
"MULT_OP", |
||||
"MATCH_MOD", |
||||
"ALERT", |
||||
"IF", |
||||
"FOR", |
||||
"WITH", |
||||
"SUMMARY", |
||||
"DESCRIPTION", |
||||
"'='", |
||||
} |
||||
var yyStatenames = []string{} |
||||
|
||||
const yyEofCode = 1 |
||||
const yyErrCode = 2 |
||||
const yyMaxDepth = 200 |
||||
|
||||
//line parser.y:281
|
||||
|
||||
//line yacctab:1
|
||||
var yyExca = []int{ |
||||
-1, 1, |
||||
1, -1, |
||||
-2, 0, |
||||
-1, 4, |
||||
1, 1, |
||||
-2, 10, |
||||
} |
||||
|
||||
const yyNprod = 56 |
||||
const yyPrivate = 57344 |
||||
|
||||
var yyTokenNames []string |
||||
var yyStates []string |
||||
|
||||
const yyLast = 159 |
||||
|
||||
var yyAct = []int{ |
||||
|
||||
78, 61, 83, 58, 55, 54, 31, 48, 6, 25, |
||||
20, 21, 23, 21, 10, 56, 64, 14, 12, 10, |
||||
56, 19, 14, 12, 11, 19, 13, 19, 92, 11, |
||||
113, 13, 22, 20, 21, 57, 8, 32, 109, 7, |
||||
53, 8, 77, 65, 7, 67, 68, 101, 19, 22, |
||||
20, 21, 70, 69, 10, 98, 30, 14, 12, 22, |
||||
20, 21, 94, 95, 11, 19, 13, 87, 85, 92, |
||||
96, 99, 86, 84, 76, 19, 8, 66, 60, 7, |
||||
29, 88, 90, 89, 24, 93, 22, 20, 21, 22, |
||||
20, 21, 92, 100, 91, 75, 82, 74, 103, 73, |
||||
43, 42, 19, 44, 43, 19, 26, 108, 62, 47, |
||||
111, 28, 80, 51, 114, 110, 38, 105, 63, 46, |
||||
18, 107, 39, 9, 49, 59, 32, 33, 35, 50, |
||||
17, 14, 106, 72, 37, 115, 112, 104, 40, 41, |
||||
34, 71, 79, 84, 102, 26, 36, 2, 3, 15, |
||||
5, 4, 1, 45, 97, 16, 27, 81, 52, |
||||
} |
||||
var yyPact = []int{ |
||||
|
||||
143, -1000, -1000, 48, 109, -1000, 72, 48, 139, 83, |
||||
49, 25, -1000, 117, -1000, -1000, 122, 140, -1000, 126, |
||||
107, 107, 107, 69, 74, -1000, 92, 110, 100, 8, |
||||
48, 112, 47, -1000, 80, -1000, 96, -18, 48, 46, |
||||
48, 48, -1000, 139, 110, 134, -1000, -1000, -1000, 125, |
||||
-1000, 70, 65, -1000, -1000, 72, -1000, 42, 11, -1000, |
||||
136, 85, 67, 48, 110, -6, 136, -12, -8, -1000, |
||||
-1000, -1000, -1000, -1000, -1000, 13, 114, 48, 62, -1000, |
||||
48, 33, -1000, -1000, 43, 32, -1000, 39, -1000, 112, |
||||
15, -1000, 138, 72, -1000, 137, 130, 93, 124, 101, |
||||
-1000, -1000, -1000, -1000, -1000, 80, -1000, 7, 90, 136, |
||||
129, -2, 88, -1000, 128, -1000, |
||||
} |
||||
var yyPgo = []int{ |
||||
|
||||
0, 158, 0, 6, 2, 157, 1, 9, 84, 156, |
||||
116, 4, 5, 155, 3, 154, 123, 153, 7, 152, |
||||
151, 150, 149, |
||||
} |
||||
var yyR1 = []int{ |
||||
|
||||
0, 19, 19, 20, 20, 21, 22, 22, 15, 15, |
||||
13, 13, 16, 16, 6, 6, 6, 5, 5, 4, |
||||
9, 9, 9, 8, 8, 7, 17, 17, 18, 18, |
||||
11, 11, 11, 11, 11, 11, 11, 11, 11, 11, |
||||
11, 11, 11, 14, 14, 10, 10, 10, 3, 3, |
||||
2, 2, 1, 1, 12, 12, |
||||
} |
||||
var yyR2 = []int{ |
||||
|
||||
0, 2, 2, 0, 2, 1, 5, 11, 0, 2, |
||||
0, 1, 1, 1, 0, 3, 2, 1, 3, 3, |
||||
0, 2, 3, 1, 3, 3, 1, 1, 0, 2, |
||||
3, 4, 3, 4, 3, 5, 6, 6, 4, 4, |
||||
4, 1, 2, 0, 1, 0, 4, 8, 0, 4, |
||||
1, 3, 1, 3, 1, 1, |
||||
} |
||||
var yyChk = []int{ |
||||
|
||||
-1000, -19, 4, 5, -20, -21, -11, 31, 28, -16, |
||||
6, 16, 10, 18, 9, -22, -13, 21, 11, 33, |
||||
18, 19, 17, -11, -8, -7, 6, -9, 28, 31, |
||||
31, -3, 12, 10, -16, 6, 6, 8, -10, 15, |
||||
-10, -10, 32, 30, 29, -17, 27, 17, -18, 14, |
||||
29, -8, -1, 32, -12, -11, 7, -11, -14, 13, |
||||
31, -6, 28, 22, 34, -11, 31, -11, -11, -7, |
||||
-18, 7, 8, 29, 32, 30, 32, 31, -2, 6, |
||||
27, -5, 29, -4, 6, -11, -18, -2, -12, -3, |
||||
-11, 32, 30, -11, 29, 30, 27, -15, 23, 32, |
||||
-14, 32, 6, -4, 7, 24, 8, 20, -6, 31, |
||||
25, -2, 7, 32, 26, 7, |
||||
} |
||||
var yyDef = []int{ |
||||
|
||||
0, -2, 3, 0, -2, 2, 5, 0, 0, 20, |
||||
13, 48, 41, 0, 12, 4, 0, 0, 11, 0, |
||||
45, 45, 45, 0, 0, 23, 0, 28, 0, 0, |
||||
0, 43, 0, 42, 14, 13, 0, 0, 0, 0, |
||||
0, 0, 30, 0, 28, 0, 26, 27, 32, 0, |
||||
21, 0, 0, 34, 52, 54, 55, 0, 0, 44, |
||||
0, 0, 0, 0, 28, 38, 0, 39, 40, 24, |
||||
31, 25, 29, 22, 33, 0, 48, 0, 0, 50, |
||||
0, 0, 16, 17, 0, 8, 35, 0, 53, 43, |
||||
0, 49, 0, 6, 15, 0, 0, 0, 0, 46, |
||||
36, 37, 51, 18, 19, 14, 9, 0, 0, 0, |
||||
0, 0, 0, 47, 0, 7, |
||||
} |
||||
var yyTok1 = []int{ |
||||
|
||||
1, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
||||
31, 32, 3, 3, 30, 3, 3, 3, 3, 3, |
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
||||
3, 27, 3, 3, 3, 3, 3, 3, 3, 3, |
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
||||
3, 33, 3, 34, 3, 3, 3, 3, 3, 3, |
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
||||
3, 3, 3, 28, 3, 29, |
||||
} |
||||
var yyTok2 = []int{ |
||||
|
||||
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, |
||||
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, |
||||
22, 23, 24, 25, 26, |
||||
} |
||||
var yyTok3 = []int{ |
||||
0, |
||||
} |
||||
|
||||
//line yaccpar:1
|
||||
|
||||
/* parser for yacc output */ |
||||
|
||||
var yyDebug = 0 |
||||
|
||||
type yyLexer interface { |
||||
Lex(lval *yySymType) int |
||||
Error(s string) |
||||
} |
||||
|
||||
const yyFlag = -1000 |
||||
|
||||
func yyTokname(c int) string { |
||||
// 4 is TOKSTART above
|
||||
if c >= 4 && c-4 < len(yyToknames) { |
||||
if yyToknames[c-4] != "" { |
||||
return yyToknames[c-4] |
||||
} |
||||
} |
||||
return __yyfmt__.Sprintf("tok-%v", c) |
||||
} |
||||
|
||||
func yyStatname(s int) string { |
||||
if s >= 0 && s < len(yyStatenames) { |
||||
if yyStatenames[s] != "" { |
||||
return yyStatenames[s] |
||||
} |
||||
} |
||||
return __yyfmt__.Sprintf("state-%v", s) |
||||
} |
||||
|
||||
func yylex1(lex yyLexer, lval *yySymType) int { |
||||
c := 0 |
||||
char := lex.Lex(lval) |
||||
if char <= 0 { |
||||
c = yyTok1[0] |
||||
goto out |
||||
} |
||||
if char < len(yyTok1) { |
||||
c = yyTok1[char] |
||||
goto out |
||||
} |
||||
if char >= yyPrivate { |
||||
if char < yyPrivate+len(yyTok2) { |
||||
c = yyTok2[char-yyPrivate] |
||||
goto out |
||||
} |
||||
} |
||||
for i := 0; i < len(yyTok3); i += 2 { |
||||
c = yyTok3[i+0] |
||||
if c == char { |
||||
c = yyTok3[i+1] |
||||
goto out |
||||
} |
||||
} |
||||
|
||||
out: |
||||
if c == 0 { |
||||
c = yyTok2[1] /* unknown char */ |
||||
} |
||||
if yyDebug >= 3 { |
||||
__yyfmt__.Printf("lex %s(%d)\n", yyTokname(c), uint(char)) |
||||
} |
||||
return c |
||||
} |
||||
|
||||
func yyParse(yylex yyLexer) int { |
||||
var yyn int |
||||
var yylval yySymType |
||||
var yyVAL yySymType |
||||
yyS := make([]yySymType, yyMaxDepth) |
||||
|
||||
Nerrs := 0 /* number of errors */ |
||||
Errflag := 0 /* error recovery flag */ |
||||
yystate := 0 |
||||
yychar := -1 |
||||
yyp := -1 |
||||
goto yystack |
||||
|
||||
ret0: |
||||
return 0 |
||||
|
||||
ret1: |
||||
return 1 |
||||
|
||||
yystack: |
||||
/* put a state and value onto the stack */ |
||||
if yyDebug >= 4 { |
||||
__yyfmt__.Printf("char %v in %v\n", yyTokname(yychar), yyStatname(yystate)) |
||||
} |
||||
|
||||
yyp++ |
||||
if yyp >= len(yyS) { |
||||
nyys := make([]yySymType, len(yyS)*2) |
||||
copy(nyys, yyS) |
||||
yyS = nyys |
||||
} |
||||
yyS[yyp] = yyVAL |
||||
yyS[yyp].yys = yystate |
||||
|
||||
yynewstate: |
||||
yyn = yyPact[yystate] |
||||
if yyn <= yyFlag { |
||||
goto yydefault /* simple state */ |
||||
} |
||||
if yychar < 0 { |
||||
yychar = yylex1(yylex, &yylval) |
||||
} |
||||
yyn += yychar |
||||
if yyn < 0 || yyn >= yyLast { |
||||
goto yydefault |
||||
} |
||||
yyn = yyAct[yyn] |
||||
if yyChk[yyn] == yychar { /* valid shift */ |
||||
yychar = -1 |
||||
yyVAL = yylval |
||||
yystate = yyn |
||||
if Errflag > 0 { |
||||
Errflag-- |
||||
} |
||||
goto yystack |
||||
} |
||||
|
||||
yydefault: |
||||
/* default state action */ |
||||
yyn = yyDef[yystate] |
||||
if yyn == -2 { |
||||
if yychar < 0 { |
||||
yychar = yylex1(yylex, &yylval) |
||||
} |
||||
|
||||
/* look through exception table */ |
||||
xi := 0 |
||||
for { |
||||
if yyExca[xi+0] == -1 && yyExca[xi+1] == yystate { |
||||
break |
||||
} |
||||
xi += 2 |
||||
} |
||||
for xi += 2; ; xi += 2 { |
||||
yyn = yyExca[xi+0] |
||||
if yyn < 0 || yyn == yychar { |
||||
break |
||||
} |
||||
} |
||||
yyn = yyExca[xi+1] |
||||
if yyn < 0 { |
||||
goto ret0 |
||||
} |
||||
} |
||||
if yyn == 0 { |
||||
/* error ... attempt to resume parsing */ |
||||
switch Errflag { |
||||
case 0: /* brand new error */ |
||||
yylex.Error("syntax error") |
||||
Nerrs++ |
||||
if yyDebug >= 1 { |
||||
__yyfmt__.Printf("%s", yyStatname(yystate)) |
||||
__yyfmt__.Printf(" saw %s\n", yyTokname(yychar)) |
||||
} |
||||
fallthrough |
||||
|
||||
case 1, 2: /* incompletely recovered error ... try again */ |
||||
Errflag = 3 |
||||
|
||||
/* find a state where "error" is a legal shift action */ |
||||
for yyp >= 0 { |
||||
yyn = yyPact[yyS[yyp].yys] + yyErrCode |
||||
if yyn >= 0 && yyn < yyLast { |
||||
yystate = yyAct[yyn] /* simulate a shift of "error" */ |
||||
if yyChk[yystate] == yyErrCode { |
||||
goto yystack |
||||
} |
||||
} |
||||
|
||||
/* the current p has no shift on "error", pop stack */ |
||||
if yyDebug >= 2 { |
||||
__yyfmt__.Printf("error recovery pops state %d\n", yyS[yyp].yys) |
||||
} |
||||
yyp-- |
||||
} |
||||
/* there is no state on the stack with an error shift ... abort */ |
||||
goto ret1 |
||||
|
||||
case 3: /* no shift yet; clobber input char */ |
||||
if yyDebug >= 2 { |
||||
__yyfmt__.Printf("error recovery discards %s\n", yyTokname(yychar)) |
||||
} |
||||
if yychar == yyEofCode { |
||||
goto ret1 |
||||
} |
||||
yychar = -1 |
||||
goto yynewstate /* try again in the same state */ |
||||
} |
||||
} |
||||
|
||||
/* reduction by production yyn */ |
||||
if yyDebug >= 2 { |
||||
__yyfmt__.Printf("reduce %v in:\n\t%v\n", yyn, yyStatname(yystate)) |
||||
} |
||||
|
||||
yynt := yyn |
||||
yypt := yyp |
||||
_ = yypt // guard against "declared and not used"
|
||||
|
||||
yyp -= yyR2[yyn] |
||||
yyVAL = yyS[yyp+1] |
||||
|
||||
/* consult goto table to find next state */ |
||||
yyn = yyR1[yyn] |
||||
yyg := yyPgo[yyn] |
||||
yyj := yyg + yyS[yyp].yys + 1 |
||||
|
||||
if yyj >= yyLast { |
||||
yystate = yyAct[yyg] |
||||
} else { |
||||
yystate = yyAct[yyj] |
||||
if yyChk[yystate] != -yyn { |
||||
yystate = yyAct[yyg] |
||||
} |
||||
} |
||||
// dummy call; replaced with literal code
|
||||
switch yynt { |
||||
|
||||
case 5: |
||||
//line parser.y:76
|
||||
{ |
||||
yylex.(*RulesLexer).parsedExpr = yyS[yypt-0].ruleNode |
||||
} |
||||
case 6: |
||||
//line parser.y:81
|
||||
{ |
||||
rule, err := CreateRecordingRule(yyS[yypt-3].str, yyS[yypt-2].labelSet, yyS[yypt-0].ruleNode, yyS[yypt-4].boolean) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule) |
||||
} |
||||
case 7: |
||||
//line parser.y:87
|
||||
{ |
||||
rule, err := CreateAlertingRule(yyS[yypt-9].str, yyS[yypt-7].ruleNode, yyS[yypt-6].str, yyS[yypt-4].labelSet, yyS[yypt-2].str, yyS[yypt-0].str) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule) |
||||
} |
||||
case 8: |
||||
//line parser.y:95
|
||||
{ |
||||
yyVAL.str = "0s" |
||||
} |
||||
case 9: |
||||
//line parser.y:97
|
||||
{ |
||||
yyVAL.str = yyS[yypt-0].str |
||||
} |
||||
case 10: |
||||
//line parser.y:101
|
||||
{ |
||||
yyVAL.boolean = false |
||||
} |
||||
case 11: |
||||
//line parser.y:103
|
||||
{ |
||||
yyVAL.boolean = true |
||||
} |
||||
case 12: |
||||
//line parser.y:107
|
||||
{ |
||||
yyVAL.str = yyS[yypt-0].str |
||||
} |
||||
case 13: |
||||
//line parser.y:109
|
||||
{ |
||||
yyVAL.str = yyS[yypt-0].str |
||||
} |
||||
case 14: |
||||
//line parser.y:113
|
||||
{ |
||||
yyVAL.labelSet = clientmodel.LabelSet{} |
||||
} |
||||
case 15: |
||||
//line parser.y:115
|
||||
{ |
||||
yyVAL.labelSet = yyS[yypt-1].labelSet |
||||
} |
||||
case 16: |
||||
//line parser.y:117
|
||||
{ |
||||
yyVAL.labelSet = clientmodel.LabelSet{} |
||||
} |
||||
case 17: |
||||
//line parser.y:120
|
||||
{ |
||||
yyVAL.labelSet = yyS[yypt-0].labelSet |
||||
} |
||||
case 18: |
||||
//line parser.y:122
|
||||
{ |
||||
for k, v := range yyS[yypt-0].labelSet { |
||||
yyVAL.labelSet[k] = v |
||||
} |
||||
} |
||||
case 19: |
||||
//line parser.y:126
|
||||
{ |
||||
yyVAL.labelSet = clientmodel.LabelSet{clientmodel.LabelName(yyS[yypt-2].str): clientmodel.LabelValue(yyS[yypt-0].str)} |
||||
} |
||||
case 20: |
||||
//line parser.y:130
|
||||
{ |
||||
yyVAL.labelMatchers = metric.LabelMatchers{} |
||||
} |
||||
case 21: |
||||
//line parser.y:132
|
||||
{ |
||||
yyVAL.labelMatchers = metric.LabelMatchers{} |
||||
} |
||||
case 22: |
||||
//line parser.y:134
|
||||
{ |
||||
yyVAL.labelMatchers = yyS[yypt-1].labelMatchers |
||||
} |
||||
case 23: |
||||
//line parser.y:138
|
||||
{ |
||||
yyVAL.labelMatchers = metric.LabelMatchers{yyS[yypt-0].labelMatcher} |
||||
} |
||||
case 24: |
||||
//line parser.y:140
|
||||
{ |
||||
yyVAL.labelMatchers = append(yyVAL.labelMatchers, yyS[yypt-0].labelMatcher) |
||||
} |
||||
case 25: |
||||
//line parser.y:144
|
||||
{ |
||||
var err error |
||||
yyVAL.labelMatcher, err = newLabelMatcher(yyS[yypt-1].str, clientmodel.LabelName(yyS[yypt-2].str), clientmodel.LabelValue(yyS[yypt-0].str)) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
} |
||||
case 26: |
||||
//line parser.y:152
|
||||
{ |
||||
yyVAL.str = "=" |
||||
} |
||||
case 27: |
||||
//line parser.y:154
|
||||
{ |
||||
yyVAL.str = yyS[yypt-0].str |
||||
} |
||||
case 28: |
||||
//line parser.y:158
|
||||
{ |
||||
yyVAL.str = "0s" |
||||
} |
||||
case 29: |
||||
//line parser.y:160
|
||||
{ |
||||
yyVAL.str = yyS[yypt-0].str |
||||
} |
||||
case 30: |
||||
//line parser.y:164
|
||||
{ |
||||
yyVAL.ruleNode = yyS[yypt-1].ruleNode |
||||
} |
||||
case 31: |
||||
//line parser.y:166
|
||||
{ |
||||
var err error |
||||
yyVAL.ruleNode, err = NewVectorSelector(yyS[yypt-2].labelMatchers, yyS[yypt-0].str) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
} |
||||
case 32: |
||||
//line parser.y:172
|
||||
{ |
||||
var err error |
||||
m, err := metric.NewLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, clientmodel.LabelValue(yyS[yypt-2].str)) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
yyS[yypt-1].labelMatchers = append(yyS[yypt-1].labelMatchers, m) |
||||
yyVAL.ruleNode, err = NewVectorSelector(yyS[yypt-1].labelMatchers, yyS[yypt-0].str) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
} |
||||
case 33: |
||||
//line parser.y:181
|
||||
{ |
||||
var err error |
||||
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-3].str, yyS[yypt-1].ruleNodeSlice) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
} |
||||
case 34: |
||||
//line parser.y:187
|
||||
{ |
||||
var err error |
||||
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-2].str, []ast.Node{}) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
} |
||||
case 35: |
||||
//line parser.y:193
|
||||
{ |
||||
var err error |
||||
yyVAL.ruleNode, err = NewMatrixSelector(yyS[yypt-4].ruleNode, yyS[yypt-2].str, yyS[yypt-0].str) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
} |
||||
case 36: |
||||
//line parser.y:199
|
||||
{ |
||||
var err error |
||||
yyVAL.ruleNode, err = NewVectorAggregation(yyS[yypt-5].str, yyS[yypt-3].ruleNode, yyS[yypt-1].labelNameSlice, yyS[yypt-0].boolean) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
} |
||||
case 37: |
||||
//line parser.y:205
|
||||
{ |
||||
var err error |
||||
yyVAL.ruleNode, err = NewVectorAggregation(yyS[yypt-5].str, yyS[yypt-1].ruleNode, yyS[yypt-4].labelNameSlice, yyS[yypt-3].boolean) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
} |
||||
case 38: |
||||
//line parser.y:213
|
||||
{ |
||||
var err error |
||||
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-2].str, yyS[yypt-3].ruleNode, yyS[yypt-0].ruleNode, yyS[yypt-1].vectorMatching) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
} |
||||
case 39: |
||||
//line parser.y:219
|
||||
{ |
||||
var err error |
||||
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-2].str, yyS[yypt-3].ruleNode, yyS[yypt-0].ruleNode, yyS[yypt-1].vectorMatching) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
} |
||||
case 40: |
||||
//line parser.y:225
|
||||
{ |
||||
var err error |
||||
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-2].str, yyS[yypt-3].ruleNode, yyS[yypt-0].ruleNode, yyS[yypt-1].vectorMatching) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
} |
||||
case 41: |
||||
//line parser.y:231
|
||||
{ |
||||
yyVAL.ruleNode = NewScalarLiteral(yyS[yypt-0].num, "+") |
||||
} |
||||
case 42: |
||||
//line parser.y:233
|
||||
{ |
||||
yyVAL.ruleNode = NewScalarLiteral(yyS[yypt-0].num, yyS[yypt-1].str) |
||||
} |
||||
case 43: |
||||
//line parser.y:237
|
||||
{ |
||||
yyVAL.boolean = false |
||||
} |
||||
case 44: |
||||
//line parser.y:239
|
||||
{ |
||||
yyVAL.boolean = true |
||||
} |
||||
case 45: |
||||
//line parser.y:243
|
||||
{ |
||||
yyVAL.vectorMatching = nil |
||||
} |
||||
case 46: |
||||
//line parser.y:245
|
||||
{ |
||||
var err error |
||||
yyVAL.vectorMatching, err = newVectorMatching("", yyS[yypt-1].labelNameSlice, nil) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
} |
||||
case 47: |
||||
//line parser.y:251
|
||||
{ |
||||
var err error |
||||
yyVAL.vectorMatching, err = newVectorMatching(yyS[yypt-3].str, yyS[yypt-5].labelNameSlice, yyS[yypt-1].labelNameSlice) |
||||
if err != nil { |
||||
yylex.Error(err.Error()) |
||||
return 1 |
||||
} |
||||
} |
||||
case 48: |
||||
//line parser.y:259
|
||||
{ |
||||
yyVAL.labelNameSlice = clientmodel.LabelNames{} |
||||
} |
||||
case 49: |
||||
//line parser.y:261
|
||||
{ |
||||
yyVAL.labelNameSlice = yyS[yypt-1].labelNameSlice |
||||
} |
||||
case 50: |
||||
//line parser.y:265
|
||||
{ |
||||
yyVAL.labelNameSlice = clientmodel.LabelNames{clientmodel.LabelName(yyS[yypt-0].str)} |
||||
} |
||||
case 51: |
||||
//line parser.y:267
|
||||
{ |
||||
yyVAL.labelNameSlice = append(yyVAL.labelNameSlice, clientmodel.LabelName(yyS[yypt-0].str)) |
||||
} |
||||
case 52: |
||||
//line parser.y:271
|
||||
{ |
||||
yyVAL.ruleNodeSlice = []ast.Node{yyS[yypt-0].ruleNode} |
||||
} |
||||
case 53: |
||||
//line parser.y:273
|
||||
{ |
||||
yyVAL.ruleNodeSlice = append(yyVAL.ruleNodeSlice, yyS[yypt-0].ruleNode) |
||||
} |
||||
case 54: |
||||
//line parser.y:277
|
||||
{ |
||||
yyVAL.ruleNode = yyS[yypt-0].ruleNode |
||||
} |
||||
case 55: |
||||
//line parser.y:279
|
||||
{ |
||||
yyVAL.ruleNode = ast.NewStringLiteral(yyS[yypt-0].str) |
||||
} |
||||
} |
||||
goto yystack /* stack new state and value */ |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue