Merge pull request #1793 from prometheus/count_values
Add count_values() aggregator.
This commit is contained in:
commit
f8bb0ee91f
|
@ -1076,6 +1076,13 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, without
|
||||||
return vector{}
|
return vector{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var valueLabel model.LabelName
|
||||||
|
if op == itemCountValues {
|
||||||
|
valueLabel = model.LabelName(ev.evalString(param).Value)
|
||||||
|
if !without {
|
||||||
|
grouping = append(grouping, valueLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, s := range vec {
|
for _, s := range vec {
|
||||||
withoutMetric := s.Metric
|
withoutMetric := s.Metric
|
||||||
|
@ -1084,6 +1091,13 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, without
|
||||||
withoutMetric.Del(l)
|
withoutMetric.Del(l)
|
||||||
}
|
}
|
||||||
withoutMetric.Del(model.MetricNameLabel)
|
withoutMetric.Del(model.MetricNameLabel)
|
||||||
|
if op == itemCountValues {
|
||||||
|
withoutMetric.Set(valueLabel, model.LabelValue(s.Value.String()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if op == itemCountValues {
|
||||||
|
s.Metric.Set(valueLabel, model.LabelValue(s.Value.String()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var groupingKey uint64
|
var groupingKey uint64
|
||||||
|
@ -1147,7 +1161,7 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, without
|
||||||
if groupedResult.value > s.Value || math.IsNaN(float64(groupedResult.value)) {
|
if groupedResult.value > s.Value || math.IsNaN(float64(groupedResult.value)) {
|
||||||
groupedResult.value = s.Value
|
groupedResult.value = s.Value
|
||||||
}
|
}
|
||||||
case itemCount:
|
case itemCount, itemCountValues:
|
||||||
groupedResult.groupCount++
|
groupedResult.groupCount++
|
||||||
case itemStdvar, itemStddev:
|
case itemStdvar, itemStddev:
|
||||||
groupedResult.value += s.Value
|
groupedResult.value += s.Value
|
||||||
|
@ -1179,7 +1193,7 @@ func (ev *evaluator) aggregation(op itemType, grouping model.LabelNames, without
|
||||||
switch op {
|
switch op {
|
||||||
case itemAvg:
|
case itemAvg:
|
||||||
aggr.value = aggr.value / model.SampleValue(aggr.groupCount)
|
aggr.value = aggr.value / model.SampleValue(aggr.groupCount)
|
||||||
case itemCount:
|
case itemCount, itemCountValues:
|
||||||
aggr.value = model.SampleValue(aggr.groupCount)
|
aggr.value = model.SampleValue(aggr.groupCount)
|
||||||
case itemStdvar:
|
case itemStdvar:
|
||||||
avg := float64(aggr.value) / float64(aggr.groupCount)
|
avg := float64(aggr.value) / float64(aggr.groupCount)
|
||||||
|
|
|
@ -58,7 +58,9 @@ func (i itemType) isAggregator() bool { return i > aggregatorsStart && i < aggre
|
||||||
|
|
||||||
// isAggregator returns true if the item is an aggregator that takes a parameter.
|
// isAggregator returns true if the item is an aggregator that takes a parameter.
|
||||||
// Returns false otherwise
|
// Returns false otherwise
|
||||||
func (i itemType) isAggregatorWithParam() bool { return i == itemTopK || i == itemBottomK }
|
func (i itemType) isAggregatorWithParam() bool {
|
||||||
|
return i == itemTopK || i == itemBottomK || i == itemCountValues
|
||||||
|
}
|
||||||
|
|
||||||
// isKeyword returns true if the item corresponds to a keyword.
|
// isKeyword returns true if the item corresponds to a keyword.
|
||||||
// Returns false otherwise.
|
// Returns false otherwise.
|
||||||
|
@ -174,6 +176,7 @@ const (
|
||||||
itemStdvar
|
itemStdvar
|
||||||
itemTopK
|
itemTopK
|
||||||
itemBottomK
|
itemBottomK
|
||||||
|
itemCountValues
|
||||||
aggregatorsEnd
|
aggregatorsEnd
|
||||||
|
|
||||||
keywordsStart
|
keywordsStart
|
||||||
|
@ -211,6 +214,7 @@ var key = map[string]itemType{
|
||||||
"stdvar": itemStdvar,
|
"stdvar": itemStdvar,
|
||||||
"topk": itemTopK,
|
"topk": itemTopK,
|
||||||
"bottomk": itemBottomK,
|
"bottomk": itemBottomK,
|
||||||
|
"count_values": itemCountValues,
|
||||||
|
|
||||||
// Keywords.
|
// Keywords.
|
||||||
"alert": itemAlert,
|
"alert": itemAlert,
|
||||||
|
|
|
@ -1052,6 +1052,9 @@ func (p *parser) checkType(node Node) (typ model.ValueType) {
|
||||||
if n.Op == itemTopK || n.Op == itemBottomK {
|
if n.Op == itemTopK || n.Op == itemBottomK {
|
||||||
p.expectType(n.Param, model.ValScalar, "aggregation parameter")
|
p.expectType(n.Param, model.ValScalar, "aggregation parameter")
|
||||||
}
|
}
|
||||||
|
if n.Op == itemCountValues {
|
||||||
|
p.expectType(n.Param, model.ValString, "aggregation parameter")
|
||||||
|
}
|
||||||
|
|
||||||
case *BinaryExpr:
|
case *BinaryExpr:
|
||||||
lt := p.checkType(n.LHS)
|
lt := p.checkType(n.LHS)
|
||||||
|
|
|
@ -1213,6 +1213,18 @@ var testExpr = []struct {
|
||||||
},
|
},
|
||||||
Param: &NumberLiteral{5},
|
Param: &NumberLiteral{5},
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
input: "count_values(\"value\", some_metric)",
|
||||||
|
expected: &AggregateExpr{
|
||||||
|
Op: itemCountValues,
|
||||||
|
Expr: &VectorSelector{
|
||||||
|
Name: "some_metric",
|
||||||
|
LabelMatchers: metric.LabelMatchers{
|
||||||
|
{Type: metric.Equal, Name: model.MetricNameLabel, Value: "some_metric"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Param: &StringLiteral{"value"},
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
input: `sum some_metric by (test)`,
|
input: `sum some_metric by (test)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
|
@ -1257,6 +1269,10 @@ var testExpr = []struct {
|
||||||
input: `topk(some_metric, other_metric)`,
|
input: `topk(some_metric, other_metric)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "parse error at char 32: expected type scalar in aggregation parameter, got vector",
|
errMsg: "parse error at char 32: expected type scalar in aggregation parameter, got vector",
|
||||||
|
}, {
|
||||||
|
input: `count_values(5, other_metric)`,
|
||||||
|
fail: true,
|
||||||
|
errMsg: "parse error at char 30: expected type string in aggregation parameter, got scalar",
|
||||||
},
|
},
|
||||||
// Test function calls.
|
// Test function calls.
|
||||||
{
|
{
|
||||||
|
|
|
@ -74,6 +74,9 @@ func TestExprString(t *testing.T) {
|
||||||
{
|
{
|
||||||
in: `topk(5, task:errors:rate10s{job="s"})`,
|
in: `topk(5, task:errors:rate10s{job="s"})`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
in: `count_values("value", task:errors:rate10s{job="s"})`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
in: `a - ON(b) c`,
|
in: `a - ON(b) c`,
|
||||||
},
|
},
|
||||||
|
|
|
@ -183,3 +183,40 @@ eval_ordered instant at 50m bottomk(3, http_requests{job="api-server",group="pro
|
||||||
http_requests{job="api-server", instance="0", group="production"} 100
|
http_requests{job="api-server", instance="0", group="production"} 100
|
||||||
http_requests{job="api-server", instance="1", group="production"} 200
|
http_requests{job="api-server", instance="1", group="production"} 200
|
||||||
http_requests{job="api-server", instance="2", group="production"} NaN
|
http_requests{job="api-server", instance="2", group="production"} NaN
|
||||||
|
|
||||||
|
clear
|
||||||
|
|
||||||
|
# Tests for count_values.
|
||||||
|
load 5m
|
||||||
|
version{job="api-server", instance="0", group="production"} 6
|
||||||
|
version{job="api-server", instance="1", group="production"} 6
|
||||||
|
version{job="api-server", instance="2", group="production"} 6
|
||||||
|
version{job="api-server", instance="0", group="canary"} 8
|
||||||
|
version{job="api-server", instance="1", group="canary"} 8
|
||||||
|
version{job="app-server", instance="0", group="production"} 6
|
||||||
|
version{job="app-server", instance="1", group="production"} 6
|
||||||
|
version{job="app-server", instance="0", group="canary"} 7
|
||||||
|
version{job="app-server", instance="1", group="canary"} 7
|
||||||
|
|
||||||
|
eval instant at 5m count_values("version", version)
|
||||||
|
{version="6"} 5
|
||||||
|
{version="7"} 2
|
||||||
|
{version="8"} 2
|
||||||
|
|
||||||
|
eval instant at 5m count_values without (instance)("version", version)
|
||||||
|
{job="api-server", group="production", version="6"} 3
|
||||||
|
{job="api-server", group="canary", version="8"} 2
|
||||||
|
{job="app-server", group="production", version="6"} 2
|
||||||
|
{job="app-server", group="canary", version="7"} 2
|
||||||
|
|
||||||
|
# Overwrite label with output. Don't do this.
|
||||||
|
eval instant at 5m count_values without (instance)("job", version)
|
||||||
|
{job="6", group="production"} 5
|
||||||
|
{job="8", group="canary"} 2
|
||||||
|
{job="7", group="canary"} 2
|
||||||
|
|
||||||
|
# Overwrite label with output. Don't do this.
|
||||||
|
eval instant at 5m count_values by (job, group)("job", version)
|
||||||
|
{job="6", group="production"} 5
|
||||||
|
{job="8", group="canary"} 2
|
||||||
|
{job="7", group="canary"} 2
|
||||||
|
|
Loading…
Reference in New Issue