mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-26 22:19:40 -08:00
Merge pull request #502 from mmikulicic/floor_ceil
Add floor, ceil and round functions. Closes #402
This commit is contained in:
commit
982923f0c4
|
@ -44,7 +44,7 @@ func (function *Function) CheckArgTypes(args []Node) error {
|
||||||
function.name, len(function.argTypes), len(args),
|
function.name, len(function.argTypes), len(args),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if len(function.argTypes) - function.optionalArgs > len(args) {
|
if len(function.argTypes)-function.optionalArgs > len(args) {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"too few arguments to function %v(): %v expected at least, %v given",
|
"too few arguments to function %v(): %v expected at least, %v given",
|
||||||
function.name, len(function.argTypes)-function.optionalArgs, len(args),
|
function.name, len(function.argTypes)-function.optionalArgs, len(args),
|
||||||
|
@ -291,6 +291,32 @@ func dropCommonLabelsImpl(timestamp clientmodel.Timestamp, args []Node) interfac
|
||||||
return vector
|
return vector
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === round(vector VectorNode) Vector ===
|
||||||
|
func roundImpl(timestamp clientmodel.Timestamp, args []Node) interface{} {
|
||||||
|
// round returns a number rounded to nearest integer.
|
||||||
|
// Ties are solved by rounding towards the even integer.
|
||||||
|
round := func(n float64) float64 {
|
||||||
|
if n-math.Floor(n) == 0.5 && int64(n)%2 == 0 {
|
||||||
|
n += math.Copysign(0.5, -n)
|
||||||
|
}
|
||||||
|
return math.Copysign(math.Floor(math.Abs(n)+0.5), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
places := float64(0)
|
||||||
|
if len(args) >= 2 {
|
||||||
|
places = float64(args[1].(ScalarNode).Eval(timestamp))
|
||||||
|
}
|
||||||
|
pow := math.Pow(10, float64(places))
|
||||||
|
|
||||||
|
n := args[0].(VectorNode)
|
||||||
|
vector := n.Eval(timestamp)
|
||||||
|
for _, el := range vector {
|
||||||
|
el.Metric.Delete(clientmodel.MetricNameLabel)
|
||||||
|
el.Value = clientmodel.SampleValue(round(pow*float64(el.Value)) / pow)
|
||||||
|
}
|
||||||
|
return vector
|
||||||
|
}
|
||||||
|
|
||||||
// === scalar(node VectorNode) Scalar ===
|
// === scalar(node VectorNode) Scalar ===
|
||||||
func scalarImpl(timestamp clientmodel.Timestamp, args []Node) interface{} {
|
func scalarImpl(timestamp clientmodel.Timestamp, args []Node) interface{} {
|
||||||
v := args[0].(VectorNode).Eval(timestamp)
|
v := args[0].(VectorNode).Eval(timestamp)
|
||||||
|
@ -343,6 +369,17 @@ func countOverTimeImpl(timestamp clientmodel.Timestamp, args []Node) interface{}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === 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 ===
|
// === max_over_time(matrix MatrixNode) Vector ===
|
||||||
func maxOverTimeImpl(timestamp clientmodel.Timestamp, args []Node) interface{} {
|
func maxOverTimeImpl(timestamp clientmodel.Timestamp, args []Node) interface{} {
|
||||||
return aggrOverTime(timestamp, args, func(values metric.Values) clientmodel.SampleValue {
|
return aggrOverTime(timestamp, args, func(values metric.Values) clientmodel.SampleValue {
|
||||||
|
@ -413,6 +450,17 @@ func absentImpl(timestamp clientmodel.Timestamp, args []Node) interface{} {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === 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
|
||||||
|
}
|
||||||
|
|
||||||
// === deriv(node MatrixNode) Vector ===
|
// === deriv(node MatrixNode) Vector ===
|
||||||
func derivImpl(timestamp clientmodel.Timestamp, args []Node) interface{} {
|
func derivImpl(timestamp clientmodel.Timestamp, args []Node) interface{} {
|
||||||
matrixNode := args[0].(MatrixNode)
|
matrixNode := args[0].(MatrixNode)
|
||||||
|
@ -481,6 +529,12 @@ var functions = map[string]*Function{
|
||||||
returnType: VectorType,
|
returnType: VectorType,
|
||||||
callFn: bottomkImpl,
|
callFn: bottomkImpl,
|
||||||
},
|
},
|
||||||
|
"ceil": {
|
||||||
|
name: "ceil",
|
||||||
|
argTypes: []ExprType{VectorType},
|
||||||
|
returnType: VectorType,
|
||||||
|
callFn: ceilImpl,
|
||||||
|
},
|
||||||
"count_over_time": {
|
"count_over_time": {
|
||||||
name: "count_over_time",
|
name: "count_over_time",
|
||||||
argTypes: []ExprType{MatrixType},
|
argTypes: []ExprType{MatrixType},
|
||||||
|
@ -506,6 +560,12 @@ var functions = map[string]*Function{
|
||||||
returnType: VectorType,
|
returnType: VectorType,
|
||||||
callFn: dropCommonLabelsImpl,
|
callFn: dropCommonLabelsImpl,
|
||||||
},
|
},
|
||||||
|
"floor": {
|
||||||
|
name: "floor",
|
||||||
|
argTypes: []ExprType{VectorType},
|
||||||
|
returnType: VectorType,
|
||||||
|
callFn: floorImpl,
|
||||||
|
},
|
||||||
"max_over_time": {
|
"max_over_time": {
|
||||||
name: "max_over_time",
|
name: "max_over_time",
|
||||||
argTypes: []ExprType{MatrixType},
|
argTypes: []ExprType{MatrixType},
|
||||||
|
@ -524,6 +584,13 @@ var functions = map[string]*Function{
|
||||||
returnType: VectorType,
|
returnType: VectorType,
|
||||||
callFn: rateImpl,
|
callFn: rateImpl,
|
||||||
},
|
},
|
||||||
|
"round": {
|
||||||
|
name: "round",
|
||||||
|
argTypes: []ExprType{VectorType, ScalarType},
|
||||||
|
optionalArgs: 1,
|
||||||
|
returnType: VectorType,
|
||||||
|
callFn: roundImpl,
|
||||||
|
},
|
||||||
"scalar": {
|
"scalar": {
|
||||||
name: "scalar",
|
name: "scalar",
|
||||||
argTypes: []ExprType{VectorType},
|
argTypes: []ExprType{VectorType},
|
||||||
|
|
|
@ -497,6 +497,114 @@ func TestExpressions(t *testing.T) {
|
||||||
fullRanges: 0,
|
fullRanges: 0,
|
||||||
intervalRanges: 2,
|
intervalRanges: 2,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
expr: `floor(0.004 * http_requests{group="production",job="api-server"})`,
|
||||||
|
output: []string{
|
||||||
|
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
||||||
|
`{group="production", instance="1", job="api-server"} => 0 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `ceil(0.004 * http_requests{group="production",job="api-server"})`,
|
||||||
|
output: []string{
|
||||||
|
`{group="production", instance="0", job="api-server"} => 1 @[%v]`,
|
||||||
|
`{group="production", instance="1", job="api-server"} => 1 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `round(0.004 * http_requests{group="production",job="api-server"})`,
|
||||||
|
output: []string{
|
||||||
|
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
||||||
|
`{group="production", instance="1", job="api-server"} => 1 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 2,
|
||||||
|
},
|
||||||
|
{ // Round should correctly handle negative numbers.
|
||||||
|
expr: `round(-1 * (0.004 * http_requests{group="production",job="api-server"}))`,
|
||||||
|
output: []string{
|
||||||
|
`{group="production", instance="0", job="api-server"} => -0 @[%v]`,
|
||||||
|
`{group="production", instance="1", job="api-server"} => -1 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 2,
|
||||||
|
},
|
||||||
|
{ // Round should round half to even.
|
||||||
|
expr: `round(0.005 * http_requests{group="production",job="api-server"})`,
|
||||||
|
output: []string{
|
||||||
|
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
||||||
|
`{group="production", instance="1", job="api-server"} => 1 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `round(-1 * (0.005 * http_requests{group="production",job="api-server"}))`,
|
||||||
|
output: []string{
|
||||||
|
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
||||||
|
`{group="production", instance="1", job="api-server"} => -1 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 2,
|
||||||
|
},
|
||||||
|
{ // Round should round half to even.
|
||||||
|
expr: `round(1 + 0.005 * http_requests{group="production",job="api-server"})`,
|
||||||
|
output: []string{
|
||||||
|
`{group="production", instance="0", job="api-server"} => 2 @[%v]`,
|
||||||
|
`{group="production", instance="1", job="api-server"} => 2 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `round(-1 * (1 + 0.005 * http_requests{group="production",job="api-server"}))`,
|
||||||
|
output: []string{
|
||||||
|
`{group="production", instance="0", job="api-server"} => -2 @[%v]`,
|
||||||
|
`{group="production", instance="1", job="api-server"} => -2 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 2,
|
||||||
|
},
|
||||||
|
{ // Round should accept a number of decimal places to round to.
|
||||||
|
expr: `round(0.0005 * http_requests{group="production",job="api-server"}, 1)`,
|
||||||
|
output: []string{
|
||||||
|
`{group="production", instance="0", job="api-server"} => 0 @[%v]`,
|
||||||
|
`{group="production", instance="1", job="api-server"} => 0.1 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 2,
|
||||||
|
},
|
||||||
|
{ // Round should look at last decimal place parity.
|
||||||
|
expr: `round(2.1 + 0.0005 * http_requests{group="production",job="api-server"}, 1)`,
|
||||||
|
output: []string{
|
||||||
|
`{group="production", instance="0", job="api-server"} => 2.2 @[%v]`,
|
||||||
|
`{group="production", instance="1", job="api-server"} => 2.2 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
expr: `round(5.2 + 0.0005 * http_requests{group="production",job="api-server"}, 1)`,
|
||||||
|
output: []string{
|
||||||
|
`{group="production", instance="0", job="api-server"} => 5.2 @[%v]`,
|
||||||
|
`{group="production", instance="1", job="api-server"} => 5.3 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 2,
|
||||||
|
},
|
||||||
|
{ // Round should work correctly with negative numbers and multiple decimal places.
|
||||||
|
expr: `round(-1 * (5.2 + 0.0005 * http_requests{group="production",job="api-server"}), 1)`,
|
||||||
|
output: []string{
|
||||||
|
`{group="production", instance="0", job="api-server"} => -5.2 @[%v]`,
|
||||||
|
`{group="production", instance="1", job="api-server"} => -5.3 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 2,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
expr: `avg_over_time(http_requests{group="production",job="api-server"}[1h])`,
|
expr: `avg_over_time(http_requests{group="production",job="api-server"}[1h])`,
|
||||||
output: []string{
|
output: []string{
|
||||||
|
|
Loading…
Reference in a new issue