// Copyright 2013 Prometheus Team // 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 ( "errors" "fmt" "github.com/prometheus/prometheus/model" "time" ) type Function struct { name string argTypes []ExprType returnType ExprType callFn func(timestamp *time.Time, view *viewAdapter, args []Node) interface{} } func (function *Function) CheckArgTypes(args []Node) error { if len(function.argTypes) != len(args) { return errors.New( fmt.Sprintf("Wrong number of arguments to function %v(): %v expected, %v given", function.name, len(function.argTypes), len(args))) } for idx, argType := range function.argTypes { invalidType := false var expectedType string if _, ok := args[idx].(ScalarNode); argType == SCALAR && !ok { invalidType = true expectedType = "scalar" } if _, ok := args[idx].(VectorNode); argType == VECTOR && !ok { invalidType = true expectedType = "vector" } if _, ok := args[idx].(MatrixNode); argType == MATRIX && !ok { invalidType = true expectedType = "matrix" } if _, ok := args[idx].(StringNode); argType == STRING && !ok { invalidType = true expectedType = "string" } if invalidType { return errors.New( fmt.Sprintf("Wrong type for argument %v in function %v(), expected %v", idx, function.name, expectedType)) } } return nil } // === time() === func timeImpl(timestamp *time.Time, view *viewAdapter, args []Node) interface{} { return model.SampleValue(time.Now().Unix()) } // === count(vector VectorNode) === func countImpl(timestamp *time.Time, view *viewAdapter, args []Node) interface{} { return model.SampleValue(len(args[0].(VectorNode).Eval(timestamp, view))) } // === delta(matrix MatrixNode, isCounter ScalarNode) === func deltaImpl(timestamp *time.Time, view *viewAdapter, args []Node) interface{} { matrixNode := args[0].(MatrixNode) isCounter := int(args[1].(ScalarNode).Eval(timestamp, view)) 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 > 0 { matrixValue = matrixNode.Eval(timestamp, view) } else { matrixValue = matrixNode.EvalBoundaries(timestamp, view) } for _, samples := range matrixValue { counterCorrection := model.SampleValue(0) lastValue := model.SampleValue(0) for _, sample := range samples.Values { currentValue := sample.Value if currentValue < lastValue { counterCorrection += lastValue - currentValue } lastValue = currentValue } resultValue := lastValue - samples.Values[0].Value + counterCorrection resultSample := &model.Sample{ Metric: samples.Metric, Value: resultValue, Timestamp: *timestamp, } resultVector = append(resultVector, resultSample) } return resultVector } // === rate(node *MatrixNode) === func rateImpl(timestamp *time.Time, view *viewAdapter, args []Node) interface{} { args = append(args, &ScalarLiteral{value: 1}) vector := deltaImpl(timestamp, view, args).(Vector) // TODO: could be other type of MatrixNode in the future (right now, only // MatrixLiteral exists). Find a better way of getting the duration of a // matrix, such as looking at the samples themselves. interval := args[0].(*MatrixLiteral).interval for _, sample := range vector { sample.Value /= model.SampleValue(interval / time.Second) } return vector } // === sampleVectorImpl() === func sampleVectorImpl(timestamp *time.Time, view *viewAdapter, args []Node) interface{} { return Vector{ &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "http_requests", "job": "api-server", "instance": "0", }, Value: 10, Timestamp: *timestamp, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "http_requests", "job": "api-server", "instance": "1", }, Value: 20, Timestamp: *timestamp, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "http_requests", "job": "api-server", "instance": "2", }, Value: 30, Timestamp: *timestamp, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "http_requests", "job": "api-server", "instance": "3", "group": "canary", }, Value: 40, Timestamp: *timestamp, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "http_requests", "job": "api-server", "instance": "2", "group": "canary", }, Value: 40, Timestamp: *timestamp, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "http_requests", "job": "api-server", "instance": "3", "group": "mytest", }, Value: 40, Timestamp: *timestamp, }, &model.Sample{ Metric: model.Metric{ model.MetricNameLabel: "http_requests", "job": "api-server", "instance": "3", "group": "mytest", }, Value: 40, Timestamp: *timestamp, }, } } var functions = map[string]*Function{ "time": { name: "time", argTypes: []ExprType{}, returnType: SCALAR, callFn: timeImpl, }, "count": { name: "count", argTypes: []ExprType{VECTOR}, returnType: SCALAR, callFn: countImpl, }, "delta": { name: "delta", argTypes: []ExprType{MATRIX, SCALAR}, returnType: VECTOR, callFn: deltaImpl, }, "rate": { name: "rate", argTypes: []ExprType{MATRIX}, returnType: VECTOR, callFn: rateImpl, }, "sampleVector": { name: "sampleVector", argTypes: []ExprType{}, returnType: VECTOR, callFn: sampleVectorImpl, }, } func GetFunction(name string) (*Function, error) { function, ok := functions[name] if !ok { return nil, errors.New(fmt.Sprintf("Couldn't find function %v()", name)) } return function, nil }