Merge pull request #1793 from prometheus/count_values

Add count_values() aggregator.
This commit is contained in:
Fabian Reinartz 2016-07-08 11:50:42 +02:00 committed by GitHub
commit f8bb0ee91f
6 changed files with 89 additions and 12 deletions

View File

@ -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)

View File

@ -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,

View File

@ -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)

View File

@ -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.
{ {

View File

@ -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`,
}, },

View File

@ -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