mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-25 13:44:05 -08:00
Add regex-matching support for labels.
There are four label-matching ops for selecting timeseries now: - Equal: = - NotEqual: != - RegexMatch: =~ - RegexNoMatch: !~ Instead of looking up labels by a simple clientmodel.LabelSet (basically an equals op for every key/value pair in the set), timeseries fingerprint selection is now done via a list of metric.LabelMatchers. Change-Id: I510a83f761198e80946146770ebb64e4abc3bb96
This commit is contained in:
parent
ae30453214
commit
c7c0b33d0b
|
@ -175,8 +175,8 @@ type (
|
||||||
type (
|
type (
|
||||||
// A VectorSelector represents a metric name plus labelset.
|
// A VectorSelector represents a metric name plus labelset.
|
||||||
VectorSelector struct {
|
VectorSelector struct {
|
||||||
labels clientmodel.LabelSet
|
labelMatchers metric.LabelMatchers
|
||||||
// Fingerprints are populated from labels at query analysis time.
|
// Fingerprints are populated from label matchers at query analysis time.
|
||||||
fingerprints clientmodel.Fingerprints
|
fingerprints clientmodel.Fingerprints
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,8 +211,8 @@ type (
|
||||||
// A MatrixSelector represents a metric name plus labelset and
|
// A MatrixSelector represents a metric name plus labelset and
|
||||||
// timerange.
|
// timerange.
|
||||||
MatrixSelector struct {
|
MatrixSelector struct {
|
||||||
labels clientmodel.LabelSet
|
labelMatchers metric.LabelMatchers
|
||||||
// Fingerprints are populated from labels at query
|
// Fingerprints are populated from label matchers at query
|
||||||
// analysis time.
|
// analysis time.
|
||||||
fingerprints clientmodel.Fingerprints
|
fingerprints clientmodel.Fingerprints
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
|
@ -738,9 +738,9 @@ func NewScalarLiteral(value clientmodel.SampleValue) *ScalarLiteral {
|
||||||
|
|
||||||
// NewVectorSelector returns a (not yet evaluated) VectorSelector with
|
// NewVectorSelector returns a (not yet evaluated) VectorSelector with
|
||||||
// the given LabelSet.
|
// the given LabelSet.
|
||||||
func NewVectorSelector(labels clientmodel.LabelSet) *VectorSelector {
|
func NewVectorSelector(m metric.LabelMatchers) *VectorSelector {
|
||||||
return &VectorSelector{
|
return &VectorSelector{
|
||||||
labels: labels,
|
labelMatchers: m,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -833,8 +833,8 @@ func NewArithExpr(opType BinOpType, lhs Node, rhs Node) (Node, error) {
|
||||||
// the given VectorSelector and Duration.
|
// the given VectorSelector and Duration.
|
||||||
func NewMatrixSelector(vector *VectorSelector, interval time.Duration) *MatrixSelector {
|
func NewMatrixSelector(vector *VectorSelector, interval time.Duration) *MatrixSelector {
|
||||||
return &MatrixSelector{
|
return &MatrixSelector{
|
||||||
labels: vector.labels,
|
labelMatchers: vector.labelMatchers,
|
||||||
interval: interval,
|
interval: interval,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -320,14 +320,13 @@ func (node *ScalarArithExpr) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *VectorSelector) String() string {
|
func (node *VectorSelector) String() string {
|
||||||
metricName, ok := node.labels[clientmodel.MetricNameLabel]
|
labelStrings := make([]string, 0, len(node.labelMatchers)-1)
|
||||||
if !ok {
|
var metricName clientmodel.LabelValue
|
||||||
panic("Tried to print vector without metric name")
|
for _, matcher := range node.labelMatchers {
|
||||||
}
|
if matcher.Name != clientmodel.MetricNameLabel {
|
||||||
labelStrings := make([]string, 0, len(node.labels)-1)
|
labelStrings = append(labelStrings, fmt.Sprintf("%s%s%q", matcher.Name, matcher.Type, matcher.Value))
|
||||||
for label, value := range node.labels {
|
} else {
|
||||||
if label != clientmodel.MetricNameLabel {
|
metricName = matcher.Value
|
||||||
labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,7 +356,7 @@ func (node *VectorArithExpr) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *MatrixSelector) String() string {
|
func (node *MatrixSelector) String() string {
|
||||||
vectorString := (&VectorSelector{labels: node.labels}).String()
|
vectorString := (&VectorSelector{labelMatchers: node.labelMatchers}).String()
|
||||||
intervalString := fmt.Sprintf("[%s]", utility.DurationToString(node.interval))
|
intervalString := fmt.Sprintf("[%s]", utility.DurationToString(node.interval))
|
||||||
return vectorString + intervalString
|
return vectorString + intervalString
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,9 +66,9 @@ func NewQueryAnalyzer(storage *metric.TieredStorage) *QueryAnalyzer {
|
||||||
func (analyzer *QueryAnalyzer) Visit(node Node) {
|
func (analyzer *QueryAnalyzer) Visit(node Node) {
|
||||||
switch n := node.(type) {
|
switch n := node.(type) {
|
||||||
case *VectorSelector:
|
case *VectorSelector:
|
||||||
fingerprints, err := analyzer.storage.GetFingerprintsForLabelSet(n.labels)
|
fingerprints, err := analyzer.storage.GetFingerprintsForLabelMatchers(n.labelMatchers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Error getting fingerprints for labelset %v: %v", n.labels, err)
|
glog.Errorf("Error getting fingerprints for label matchers %v: %v", n.labelMatchers, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n.fingerprints = fingerprints
|
n.fingerprints = fingerprints
|
||||||
|
@ -80,9 +80,9 @@ func (analyzer *QueryAnalyzer) Visit(node Node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *MatrixSelector:
|
case *MatrixSelector:
|
||||||
fingerprints, err := analyzer.storage.GetFingerprintsForLabelSet(n.labels)
|
fingerprints, err := analyzer.storage.GetFingerprintsForLabelMatchers(n.labelMatchers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Error getting fingerprints for labelset %v: %v", n.labels, err)
|
glog.Errorf("Error getting fingerprints for label matchers %v: %v", n.labelMatchers, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n.fingerprints = fingerprints
|
n.fingerprints = fingerprints
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
clientmodel "github.com/prometheus/client_golang/model"
|
clientmodel "github.com/prometheus/client_golang/model"
|
||||||
|
|
||||||
"github.com/prometheus/prometheus/rules/ast"
|
"github.com/prometheus/prometheus/rules/ast"
|
||||||
|
"github.com/prometheus/prometheus/storage/metric"
|
||||||
"github.com/prometheus/prometheus/utility"
|
"github.com/prometheus/prometheus/utility"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -116,6 +117,20 @@ func NewMatrixSelector(vector ast.Node, intervalStr string) (ast.MatrixNode, err
|
||||||
return ast.NewMatrixSelector(vectorSelector, interval), nil
|
return ast.NewMatrixSelector(vectorSelector, interval), 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 \"%v\"", matchTypeStr)
|
||||||
|
}
|
||||||
|
return metric.NewLabelMatcher(matchType, name, value)
|
||||||
|
}
|
||||||
|
|
||||||
func ConsoleLinkForExpression(expr string) string {
|
func ConsoleLinkForExpression(expr string) string {
|
||||||
// url.QueryEscape percent-escapes everything except spaces, for which it
|
// url.QueryEscape percent-escapes everything except spaces, for which it
|
||||||
// uses "+". However, in the non-query part of a URI, only percent-escaped
|
// uses "+". However, in the non-query part of a URI, only percent-escaped
|
||||||
|
|
|
@ -83,7 +83,7 @@ KEEPING_EXTRA|keeping_extra return KEEPING_EXTRA
|
||||||
AVG|SUM|MAX|MIN|COUNT lval.str = lexer.token(); return AGGR_OP
|
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
|
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
|
\<|>|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 CMP_OP
|
||||||
[+\-] lval.str = lexer.token(); return ADDITIVE_OP
|
[+\-] lval.str = lexer.token(); return ADDITIVE_OP
|
||||||
[*/%] lval.str = lexer.token(); return MULT_OP
|
[*/%] lval.str = lexer.token(); return MULT_OP
|
||||||
|
|
||||||
|
|
|
@ -155,7 +155,7 @@ c = lexer.getChar()
|
||||||
switch {
|
switch {
|
||||||
default:
|
default:
|
||||||
goto yyabort
|
goto yyabort
|
||||||
case c == '=':
|
case c == '=' || c == '~':
|
||||||
goto yystate4
|
goto yystate4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +326,7 @@ c = lexer.getChar()
|
||||||
switch {
|
switch {
|
||||||
default:
|
default:
|
||||||
goto yyrule26
|
goto yyrule26
|
||||||
case c == '=':
|
case c == '=' || c == '~':
|
||||||
goto yystate4
|
goto yystate4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2068,9 +2068,9 @@ yyrule16: // \<|>|AND|OR|and|or
|
||||||
lval.str = strings.ToUpper(lexer.token()); return CMP_OP
|
lval.str = strings.ToUpper(lexer.token()); return CMP_OP
|
||||||
goto yystate0
|
goto yystate0
|
||||||
}
|
}
|
||||||
yyrule17: // ==|!=|>=|<=
|
yyrule17: // ==|!=|>=|<=|=~|!~
|
||||||
{
|
{
|
||||||
lval.str = lexer.token(); return CMP_OP
|
lval.str = lexer.token(); return CMP_OP
|
||||||
goto yystate0
|
goto yystate0
|
||||||
}
|
}
|
||||||
yyrule18: // [+\-]
|
yyrule18: // [+\-]
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
clientmodel "github.com/prometheus/client_golang/model"
|
clientmodel "github.com/prometheus/client_golang/model"
|
||||||
|
|
||||||
"github.com/prometheus/prometheus/rules/ast"
|
"github.com/prometheus/prometheus/rules/ast"
|
||||||
|
"github.com/prometheus/prometheus/storage/metric"
|
||||||
)
|
)
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
@ -29,6 +30,8 @@
|
||||||
boolean bool
|
boolean bool
|
||||||
labelNameSlice clientmodel.LabelNames
|
labelNameSlice clientmodel.LabelNames
|
||||||
labelSet clientmodel.LabelSet
|
labelSet clientmodel.LabelSet
|
||||||
|
labelMatcher *metric.LabelMatcher
|
||||||
|
labelMatchers metric.LabelMatchers
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We simulate multiple start symbols for closely-related grammars via dummy tokens. See
|
/* We simulate multiple start symbols for closely-related grammars via dummy tokens. See
|
||||||
|
@ -46,9 +49,11 @@
|
||||||
%type <ruleNodeSlice> func_arg_list
|
%type <ruleNodeSlice> func_arg_list
|
||||||
%type <labelNameSlice> label_list grouping_opts
|
%type <labelNameSlice> label_list grouping_opts
|
||||||
%type <labelSet> label_assign label_assign_list rule_labels
|
%type <labelSet> label_assign label_assign_list rule_labels
|
||||||
|
%type <labelMatcher> label_match
|
||||||
|
%type <labelMatchers> label_match_list label_matches
|
||||||
%type <ruleNode> rule_expr func_arg
|
%type <ruleNode> rule_expr func_arg
|
||||||
%type <boolean> qualifier extra_labels_opts
|
%type <boolean> qualifier extra_labels_opts
|
||||||
%type <str> for_duration metric_name
|
%type <str> for_duration metric_name label_match_type
|
||||||
|
|
||||||
%right '='
|
%right '='
|
||||||
%left CMP_OP
|
%left CMP_OP
|
||||||
|
@ -119,11 +124,44 @@ label_assign : IDENTIFIER '=' STRING
|
||||||
{ $$ = clientmodel.LabelSet{ clientmodel.LabelName($1): clientmodel.LabelValue($3) } }
|
{ $$ = 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 }
|
||||||
|
;
|
||||||
|
|
||||||
rule_expr : '(' rule_expr ')'
|
rule_expr : '(' rule_expr ')'
|
||||||
{ $$ = $2 }
|
{ $$ = $2 }
|
||||||
| metric_name rule_labels
|
| metric_name label_matches
|
||||||
{ $2[clientmodel.MetricNameLabel] = clientmodel.LabelValue($1); $$ = ast.NewVectorSelector($2) }
|
{
|
||||||
|
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)
|
||||||
|
$$ = ast.NewVectorSelector($2)
|
||||||
|
}
|
||||||
| IDENTIFIER '(' func_arg_list ')'
|
| IDENTIFIER '(' func_arg_list ')'
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
|
|
|
@ -8,9 +8,10 @@ import __yyfmt__ "fmt"
|
||||||
clientmodel "github.com/prometheus/client_golang/model"
|
clientmodel "github.com/prometheus/client_golang/model"
|
||||||
|
|
||||||
"github.com/prometheus/prometheus/rules/ast"
|
"github.com/prometheus/prometheus/rules/ast"
|
||||||
|
"github.com/prometheus/prometheus/storage/metric"
|
||||||
)
|
)
|
||||||
|
|
||||||
//line parser.y:24
|
//line parser.y:25
|
||||||
type yySymType struct {
|
type yySymType struct {
|
||||||
yys int
|
yys int
|
||||||
num clientmodel.SampleValue
|
num clientmodel.SampleValue
|
||||||
|
@ -20,6 +21,8 @@ type yySymType struct {
|
||||||
boolean bool
|
boolean bool
|
||||||
labelNameSlice clientmodel.LabelNames
|
labelNameSlice clientmodel.LabelNames
|
||||||
labelSet clientmodel.LabelSet
|
labelSet clientmodel.LabelSet
|
||||||
|
labelMatcher *metric.LabelMatcher
|
||||||
|
labelMatchers metric.LabelMatchers
|
||||||
}
|
}
|
||||||
|
|
||||||
const START_RULES = 57346
|
const START_RULES = 57346
|
||||||
|
@ -72,7 +75,7 @@ const yyEofCode = 1
|
||||||
const yyErrCode = 2
|
const yyErrCode = 2
|
||||||
const yyMaxDepth = 200
|
const yyMaxDepth = 200
|
||||||
|
|
||||||
//line parser.y:204
|
//line parser.y:242
|
||||||
|
|
||||||
|
|
||||||
//line yacctab:1
|
//line yacctab:1
|
||||||
|
@ -85,79 +88,87 @@ var yyExca = []int{
|
||||||
-2, 10,
|
-2, 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
const yyNprod = 40
|
const yyNprod = 48
|
||||||
const yyPrivate = 57344
|
const yyPrivate = 57344
|
||||||
|
|
||||||
var yyTokenNames []string
|
var yyTokenNames []string
|
||||||
var yyStates []string
|
var yyStates []string
|
||||||
|
|
||||||
const yyLast = 108
|
const yyLast = 121
|
||||||
|
|
||||||
var yyAct = []int{
|
var yyAct = []int{
|
||||||
|
|
||||||
22, 40, 41, 36, 19, 46, 6, 17, 9, 42,
|
44, 59, 41, 40, 36, 47, 6, 17, 9, 42,
|
||||||
21, 12, 11, 72, 23, 71, 10, 17, 20, 18,
|
21, 12, 11, 18, 19, 84, 10, 83, 20, 18,
|
||||||
19, 30, 31, 32, 20, 18, 19, 44, 43, 62,
|
19, 30, 31, 32, 20, 18, 19, 17, 43, 72,
|
||||||
7, 39, 52, 17, 65, 20, 18, 19, 25, 17,
|
7, 39, 55, 17, 75, 25, 20, 18, 19, 17,
|
||||||
20, 18, 19, 9, 42, 24, 12, 11, 54, 33,
|
20, 18, 19, 24, 9, 42, 70, 12, 11, 61,
|
||||||
17, 10, 55, 57, 9, 17, 60, 12, 11, 18,
|
33, 17, 10, 9, 62, 17, 12, 11, 64, 67,
|
||||||
19, 51, 10, 50, 37, 7, 73, 70, 47, 48,
|
19, 10, 54, 60, 53, 37, 7, 68, 69, 45,
|
||||||
53, 49, 76, 17, 66, 45, 7, 8, 16, 64,
|
23, 76, 56, 17, 88, 7, 48, 49, 52, 82,
|
||||||
59, 67, 29, 27, 35, 15, 12, 77, 75, 56,
|
85, 78, 16, 58, 46, 34, 8, 51, 74, 15,
|
||||||
74, 69, 26, 37, 28, 2, 3, 13, 5, 4,
|
66, 27, 79, 89, 12, 29, 87, 77, 63, 86,
|
||||||
1, 61, 63, 14, 34, 58, 68, 38,
|
81, 26, 60, 37, 28, 2, 3, 13, 5, 4,
|
||||||
|
1, 50, 71, 73, 14, 22, 35, 57, 65, 80,
|
||||||
|
38,
|
||||||
}
|
}
|
||||||
var yyPact = []int{
|
var yyPact = []int{
|
||||||
|
|
||||||
91, -1000, -1000, 48, 67, -1000, 25, 48, -11, 17,
|
101, -1000, -1000, 47, 71, -1000, 25, 47, 45, 15,
|
||||||
10, -1000, -1000, -1000, 77, 88, -1000, 74, 48, 48,
|
7, -1000, -1000, -1000, 85, 98, -1000, 87, 47, 47,
|
||||||
48, 20, -1000, 58, 2, 48, -11, -1000, 56, -26,
|
47, 21, -1000, 59, 2, 47, 44, -1000, 65, -26,
|
||||||
-13, -23, 43, -1000, 42, -1000, -1000, 47, 34, -1000,
|
43, -23, -3, -1000, -1000, 50, -1000, 63, 35, -1000,
|
||||||
-1000, 25, -1000, 3, 46, 48, -1000, -1000, 87, 82,
|
-1000, 25, -1000, 3, 48, 57, 47, -1000, -1000, 97,
|
||||||
-1000, 37, 68, 48, 9, -1000, -1000, -1000, 66, 6,
|
91, -1000, -1000, -1000, 38, 78, 47, 41, -1000, -1000,
|
||||||
25, 53, 73, -1000, -1000, 85, -11, -1000, -14, -1000,
|
22, 9, -1000, -1000, -1000, 75, 6, 25, -1000, 96,
|
||||||
44, -1000, 84, 81, -1000, 49, 80, -1000,
|
90, 60, 84, -1000, -1000, 94, -1000, -1000, 44, -1000,
|
||||||
|
-12, -1000, 58, -1000, 93, 89, -1000, 51, 86, -1000,
|
||||||
}
|
}
|
||||||
var yyPgo = []int{
|
var yyPgo = []int{
|
||||||
|
|
||||||
0, 107, 106, 105, 3, 104, 0, 2, 1, 103,
|
0, 120, 119, 118, 1, 117, 0, 4, 116, 115,
|
||||||
102, 101, 77, 100, 99, 98, 97,
|
2, 3, 114, 113, 112, 86, 111, 110, 109, 108,
|
||||||
|
107,
|
||||||
}
|
}
|
||||||
var yyR1 = []int{
|
var yyR1 = []int{
|
||||||
|
|
||||||
0, 13, 13, 14, 14, 15, 16, 16, 11, 11,
|
0, 17, 17, 18, 18, 19, 20, 20, 14, 14,
|
||||||
9, 9, 12, 12, 6, 6, 6, 5, 5, 4,
|
12, 12, 15, 15, 6, 6, 6, 5, 5, 4,
|
||||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
9, 9, 9, 8, 8, 7, 16, 16, 10, 10,
|
||||||
10, 10, 3, 3, 2, 2, 1, 1, 8, 8,
|
10, 10, 10, 10, 10, 10, 10, 10, 13, 13,
|
||||||
|
3, 3, 2, 2, 1, 1, 11, 11,
|
||||||
}
|
}
|
||||||
var yyR2 = []int{
|
var yyR2 = []int{
|
||||||
|
|
||||||
0, 2, 2, 0, 2, 1, 5, 11, 0, 2,
|
0, 2, 2, 0, 2, 1, 5, 11, 0, 2,
|
||||||
0, 1, 1, 1, 0, 3, 2, 1, 3, 3,
|
0, 1, 1, 1, 0, 3, 2, 1, 3, 3,
|
||||||
3, 2, 4, 3, 4, 6, 3, 3, 3, 1,
|
0, 2, 3, 1, 3, 3, 1, 1, 3, 2,
|
||||||
0, 1, 0, 4, 1, 3, 1, 3, 1, 1,
|
4, 3, 4, 6, 3, 3, 3, 1, 0, 1,
|
||||||
|
0, 4, 1, 3, 1, 3, 1, 1,
|
||||||
}
|
}
|
||||||
var yyChk = []int{
|
var yyChk = []int{
|
||||||
|
|
||||||
-1000, -13, 4, 5, -14, -15, -7, 28, -12, 6,
|
-1000, -17, 4, 5, -18, -19, -10, 28, -15, 6,
|
||||||
14, 10, 9, -16, -9, 18, 11, 30, 16, 17,
|
14, 10, 9, -20, -12, 18, 11, 30, 16, 17,
|
||||||
15, -7, -6, 25, 28, 28, -12, 6, 6, 8,
|
15, -10, -9, 25, 28, 28, -15, 6, 6, 8,
|
||||||
-7, -7, -7, 29, -5, 26, -4, 6, -1, 29,
|
-10, -10, -10, 29, 26, -8, -7, 6, -1, 29,
|
||||||
-8, -7, 7, -7, -6, 19, 31, 26, 27, 24,
|
-11, -10, 7, -10, -6, 25, 19, 31, 26, 27,
|
||||||
29, 27, 29, 24, -7, -4, 7, -8, -3, 12,
|
-16, 24, 15, 29, 27, 29, 24, -5, 26, -4,
|
||||||
-7, -11, 20, -10, 13, 28, 21, 8, -2, 6,
|
6, -10, -7, 7, -11, -3, 12, -10, 26, 27,
|
||||||
-6, 29, 27, 22, 6, 7, 23, 7,
|
24, -14, 20, -13, 13, 28, -4, 7, 21, 8,
|
||||||
|
-2, 6, -6, 29, 27, 22, 6, 7, 23, 7,
|
||||||
}
|
}
|
||||||
var yyDef = []int{
|
var yyDef = []int{
|
||||||
|
|
||||||
0, -2, 3, 0, -2, 2, 5, 0, 14, 13,
|
0, -2, 3, 0, -2, 2, 5, 0, 20, 13,
|
||||||
0, 29, 12, 4, 0, 0, 11, 0, 0, 0,
|
0, 37, 12, 4, 0, 0, 11, 0, 0, 0,
|
||||||
0, 0, 21, 0, 0, 0, 14, 13, 0, 0,
|
0, 0, 29, 0, 0, 0, 14, 13, 0, 0,
|
||||||
26, 27, 28, 20, 0, 16, 17, 0, 0, 23,
|
34, 35, 36, 28, 21, 0, 23, 0, 0, 31,
|
||||||
36, 38, 39, 0, 0, 0, 24, 15, 0, 0,
|
44, 46, 47, 0, 0, 0, 0, 32, 22, 0,
|
||||||
22, 0, 32, 0, 8, 18, 19, 37, 30, 0,
|
0, 26, 27, 30, 0, 40, 0, 0, 16, 17,
|
||||||
6, 0, 0, 25, 31, 0, 14, 9, 0, 34,
|
0, 8, 24, 25, 45, 38, 0, 6, 15, 0,
|
||||||
0, 33, 0, 0, 35, 0, 0, 7,
|
0, 0, 0, 33, 39, 0, 18, 19, 14, 9,
|
||||||
|
0, 42, 0, 41, 0, 0, 43, 0, 0, 7,
|
||||||
}
|
}
|
||||||
var yyTok1 = []int{
|
var yyTok1 = []int{
|
||||||
|
|
||||||
|
@ -411,145 +422,179 @@ yydefault:
|
||||||
switch yynt {
|
switch yynt {
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
//line parser.y:69
|
//line parser.y:74
|
||||||
{ yylex.(*RulesLexer).parsedExpr = yyS[yypt-0].ruleNode }
|
{ yylex.(*RulesLexer).parsedExpr = yyS[yypt-0].ruleNode }
|
||||||
case 6:
|
case 6:
|
||||||
//line parser.y:74
|
//line parser.y:79
|
||||||
{
|
{
|
||||||
rule, err := CreateRecordingRule(yyS[yypt-3].str, yyS[yypt-2].labelSet, yyS[yypt-0].ruleNode, yyS[yypt-4].boolean)
|
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 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
|
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
|
||||||
}
|
}
|
||||||
case 7:
|
case 7:
|
||||||
//line parser.y:80
|
//line parser.y:85
|
||||||
{
|
{
|
||||||
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)
|
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 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
|
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
|
||||||
}
|
}
|
||||||
case 8:
|
case 8:
|
||||||
//line parser.y:88
|
//line parser.y:93
|
||||||
{ yyVAL.str = "0s" }
|
{ yyVAL.str = "0s" }
|
||||||
case 9:
|
case 9:
|
||||||
//line parser.y:90
|
//line parser.y:95
|
||||||
{ yyVAL.str = yyS[yypt-0].str }
|
{ yyVAL.str = yyS[yypt-0].str }
|
||||||
case 10:
|
case 10:
|
||||||
//line parser.y:94
|
//line parser.y:99
|
||||||
{ yyVAL.boolean = false }
|
{ yyVAL.boolean = false }
|
||||||
case 11:
|
case 11:
|
||||||
//line parser.y:96
|
//line parser.y:101
|
||||||
{ yyVAL.boolean = true }
|
{ yyVAL.boolean = true }
|
||||||
case 12:
|
case 12:
|
||||||
//line parser.y:100
|
//line parser.y:105
|
||||||
{ yyVAL.str = yyS[yypt-0].str }
|
{ yyVAL.str = yyS[yypt-0].str }
|
||||||
case 13:
|
case 13:
|
||||||
//line parser.y:102
|
//line parser.y:107
|
||||||
{ yyVAL.str = yyS[yypt-0].str }
|
{ yyVAL.str = yyS[yypt-0].str }
|
||||||
case 14:
|
case 14:
|
||||||
//line parser.y:106
|
//line parser.y:111
|
||||||
{ yyVAL.labelSet = clientmodel.LabelSet{} }
|
{ yyVAL.labelSet = clientmodel.LabelSet{} }
|
||||||
case 15:
|
case 15:
|
||||||
//line parser.y:108
|
//line parser.y:113
|
||||||
{ yyVAL.labelSet = yyS[yypt-1].labelSet }
|
{ yyVAL.labelSet = yyS[yypt-1].labelSet }
|
||||||
case 16:
|
case 16:
|
||||||
//line parser.y:110
|
//line parser.y:115
|
||||||
{ yyVAL.labelSet = clientmodel.LabelSet{} }
|
{ yyVAL.labelSet = clientmodel.LabelSet{} }
|
||||||
case 17:
|
case 17:
|
||||||
//line parser.y:113
|
//line parser.y:118
|
||||||
{ yyVAL.labelSet = yyS[yypt-0].labelSet }
|
{ yyVAL.labelSet = yyS[yypt-0].labelSet }
|
||||||
case 18:
|
case 18:
|
||||||
//line parser.y:115
|
//line parser.y:120
|
||||||
{ for k, v := range yyS[yypt-0].labelSet { yyVAL.labelSet[k] = v } }
|
{ for k, v := range yyS[yypt-0].labelSet { yyVAL.labelSet[k] = v } }
|
||||||
case 19:
|
case 19:
|
||||||
//line parser.y:119
|
//line parser.y:124
|
||||||
{ yyVAL.labelSet = clientmodel.LabelSet{ clientmodel.LabelName(yyS[yypt-2].str): clientmodel.LabelValue(yyS[yypt-0].str) } }
|
{ yyVAL.labelSet = clientmodel.LabelSet{ clientmodel.LabelName(yyS[yypt-2].str): clientmodel.LabelValue(yyS[yypt-0].str) } }
|
||||||
case 20:
|
case 20:
|
||||||
//line parser.y:124
|
|
||||||
{ yyVAL.ruleNode = yyS[yypt-1].ruleNode }
|
|
||||||
case 21:
|
|
||||||
//line parser.y:126
|
|
||||||
{ yyS[yypt-0].labelSet[clientmodel.MetricNameLabel] = clientmodel.LabelValue(yyS[yypt-1].str); yyVAL.ruleNode = ast.NewVectorSelector(yyS[yypt-0].labelSet) }
|
|
||||||
case 22:
|
|
||||||
//line parser.y:128
|
//line parser.y:128
|
||||||
|
{ yyVAL.labelMatchers = metric.LabelMatchers{} }
|
||||||
|
case 21:
|
||||||
|
//line parser.y:130
|
||||||
|
{ yyVAL.labelMatchers = metric.LabelMatchers{} }
|
||||||
|
case 22:
|
||||||
|
//line parser.y:132
|
||||||
|
{ yyVAL.labelMatchers = yyS[yypt-1].labelMatchers }
|
||||||
|
case 23:
|
||||||
|
//line parser.y:136
|
||||||
|
{ yyVAL.labelMatchers = metric.LabelMatchers{yyS[yypt-0].labelMatcher} }
|
||||||
|
case 24:
|
||||||
|
//line parser.y:138
|
||||||
|
{ yyVAL.labelMatchers = append(yyVAL.labelMatchers, yyS[yypt-0].labelMatcher) }
|
||||||
|
case 25:
|
||||||
|
//line parser.y:142
|
||||||
|
{
|
||||||
|
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:150
|
||||||
|
{ yyVAL.str = "=" }
|
||||||
|
case 27:
|
||||||
|
//line parser.y:152
|
||||||
|
{ yyVAL.str = yyS[yypt-0].str }
|
||||||
|
case 28:
|
||||||
|
//line parser.y:156
|
||||||
|
{ yyVAL.ruleNode = yyS[yypt-1].ruleNode }
|
||||||
|
case 29:
|
||||||
|
//line parser.y:158
|
||||||
|
{
|
||||||
|
var err error
|
||||||
|
m, err := metric.NewLabelMatcher(metric.Equal, clientmodel.MetricNameLabel, clientmodel.LabelValue(yyS[yypt-1].str))
|
||||||
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
|
yyS[yypt-0].labelMatchers = append(yyS[yypt-0].labelMatchers, m)
|
||||||
|
yyVAL.ruleNode = ast.NewVectorSelector(yyS[yypt-0].labelMatchers)
|
||||||
|
}
|
||||||
|
case 30:
|
||||||
|
//line parser.y:166
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-3].str, yyS[yypt-1].ruleNodeSlice)
|
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-3].str, yyS[yypt-1].ruleNodeSlice)
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
}
|
}
|
||||||
case 23:
|
case 31:
|
||||||
//line parser.y:134
|
//line parser.y:172
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-2].str, []ast.Node{})
|
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-2].str, []ast.Node{})
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
}
|
}
|
||||||
case 24:
|
case 32:
|
||||||
//line parser.y:140
|
//line parser.y:178
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
yyVAL.ruleNode, err = NewMatrixSelector(yyS[yypt-3].ruleNode, yyS[yypt-1].str)
|
yyVAL.ruleNode, err = NewMatrixSelector(yyS[yypt-3].ruleNode, yyS[yypt-1].str)
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
}
|
}
|
||||||
case 25:
|
case 33:
|
||||||
//line parser.y:146
|
//line parser.y:184
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
yyVAL.ruleNode, err = NewVectorAggregation(yyS[yypt-5].str, yyS[yypt-3].ruleNode, yyS[yypt-1].labelNameSlice, yyS[yypt-0].boolean)
|
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 }
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
}
|
}
|
||||||
case 26:
|
|
||||||
//line parser.y:154
|
|
||||||
{
|
|
||||||
var err error
|
|
||||||
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
|
||||||
}
|
|
||||||
case 27:
|
|
||||||
//line parser.y:160
|
|
||||||
{
|
|
||||||
var err error
|
|
||||||
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
|
||||||
}
|
|
||||||
case 28:
|
|
||||||
//line parser.y:166
|
|
||||||
{
|
|
||||||
var err error
|
|
||||||
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
|
||||||
if err != nil { yylex.Error(err.Error()); return 1 }
|
|
||||||
}
|
|
||||||
case 29:
|
|
||||||
//line parser.y:172
|
|
||||||
{ yyVAL.ruleNode = ast.NewScalarLiteral(yyS[yypt-0].num)}
|
|
||||||
case 30:
|
|
||||||
//line parser.y:176
|
|
||||||
{ yyVAL.boolean = false }
|
|
||||||
case 31:
|
|
||||||
//line parser.y:178
|
|
||||||
{ yyVAL.boolean = true }
|
|
||||||
case 32:
|
|
||||||
//line parser.y:182
|
|
||||||
{ yyVAL.labelNameSlice = clientmodel.LabelNames{} }
|
|
||||||
case 33:
|
|
||||||
//line parser.y:184
|
|
||||||
{ yyVAL.labelNameSlice = yyS[yypt-1].labelNameSlice }
|
|
||||||
case 34:
|
case 34:
|
||||||
//line parser.y:188
|
//line parser.y:192
|
||||||
{ yyVAL.labelNameSlice = clientmodel.LabelNames{clientmodel.LabelName(yyS[yypt-0].str)} }
|
{
|
||||||
|
var err error
|
||||||
|
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
||||||
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
|
}
|
||||||
case 35:
|
case 35:
|
||||||
//line parser.y:190
|
//line parser.y:198
|
||||||
{ yyVAL.labelNameSlice = append(yyVAL.labelNameSlice, clientmodel.LabelName(yyS[yypt-0].str)) }
|
{
|
||||||
|
var err error
|
||||||
|
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
||||||
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
|
}
|
||||||
case 36:
|
case 36:
|
||||||
//line parser.y:194
|
//line parser.y:204
|
||||||
{ yyVAL.ruleNodeSlice = []ast.Node{yyS[yypt-0].ruleNode} }
|
{
|
||||||
|
var err error
|
||||||
|
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
|
||||||
|
if err != nil { yylex.Error(err.Error()); return 1 }
|
||||||
|
}
|
||||||
case 37:
|
case 37:
|
||||||
//line parser.y:196
|
//line parser.y:210
|
||||||
{ yyVAL.ruleNodeSlice = append(yyVAL.ruleNodeSlice, yyS[yypt-0].ruleNode) }
|
{ yyVAL.ruleNode = ast.NewScalarLiteral(yyS[yypt-0].num)}
|
||||||
case 38:
|
case 38:
|
||||||
//line parser.y:200
|
//line parser.y:214
|
||||||
{ yyVAL.ruleNode = yyS[yypt-0].ruleNode }
|
{ yyVAL.boolean = false }
|
||||||
case 39:
|
case 39:
|
||||||
//line parser.y:202
|
//line parser.y:216
|
||||||
|
{ yyVAL.boolean = true }
|
||||||
|
case 40:
|
||||||
|
//line parser.y:220
|
||||||
|
{ yyVAL.labelNameSlice = clientmodel.LabelNames{} }
|
||||||
|
case 41:
|
||||||
|
//line parser.y:222
|
||||||
|
{ yyVAL.labelNameSlice = yyS[yypt-1].labelNameSlice }
|
||||||
|
case 42:
|
||||||
|
//line parser.y:226
|
||||||
|
{ yyVAL.labelNameSlice = clientmodel.LabelNames{clientmodel.LabelName(yyS[yypt-0].str)} }
|
||||||
|
case 43:
|
||||||
|
//line parser.y:228
|
||||||
|
{ yyVAL.labelNameSlice = append(yyVAL.labelNameSlice, clientmodel.LabelName(yyS[yypt-0].str)) }
|
||||||
|
case 44:
|
||||||
|
//line parser.y:232
|
||||||
|
{ yyVAL.ruleNodeSlice = []ast.Node{yyS[yypt-0].ruleNode} }
|
||||||
|
case 45:
|
||||||
|
//line parser.y:234
|
||||||
|
{ yyVAL.ruleNodeSlice = append(yyVAL.ruleNodeSlice, yyS[yypt-0].ruleNode) }
|
||||||
|
case 46:
|
||||||
|
//line parser.y:238
|
||||||
|
{ yyVAL.ruleNode = yyS[yypt-0].ruleNode }
|
||||||
|
case 47:
|
||||||
|
//line parser.y:240
|
||||||
{ yyVAL.ruleNode = ast.NewStringLiteral(yyS[yypt-0].str) }
|
{ yyVAL.ruleNode = ast.NewStringLiteral(yyS[yypt-0].str) }
|
||||||
}
|
}
|
||||||
goto yystack /* stack new state and value */
|
goto yystack /* stack new state and value */
|
||||||
|
|
|
@ -406,6 +406,47 @@ func TestExpressions(t *testing.T) {
|
||||||
// Interval durations can"t be in quotes.
|
// Interval durations can"t be in quotes.
|
||||||
expr: `http_requests["1m"]`,
|
expr: `http_requests["1m"]`,
|
||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
|
}, {
|
||||||
|
expr: `http_requests{group!="canary"}`,
|
||||||
|
output: []string{
|
||||||
|
`http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`,
|
||||||
|
`http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`,
|
||||||
|
`http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
||||||
|
`http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 4,
|
||||||
|
}, {
|
||||||
|
expr: `http_requests{job=~"server",group!="canary"}`,
|
||||||
|
output: []string{
|
||||||
|
`http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`,
|
||||||
|
`http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`,
|
||||||
|
`http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
||||||
|
`http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 4,
|
||||||
|
}, {
|
||||||
|
expr: `http_requests{job!~"api",group!="canary"}`,
|
||||||
|
output: []string{
|
||||||
|
`http_requests{group="production", instance="1", job="app-server"} => 600 @[%v]`,
|
||||||
|
`http_requests{group="production", instance="0", job="app-server"} => 500 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 2,
|
||||||
|
}, {
|
||||||
|
expr: `count_scalar(http_requests{job=~"^server$"})`,
|
||||||
|
output: []string{`scalar: 0 @[%v]`},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 0,
|
||||||
|
}, {
|
||||||
|
expr: `http_requests{group="production",job=~"^api"}`,
|
||||||
|
output: []string{
|
||||||
|
`http_requests{group="production", instance="1", job="api-server"} => 200 @[%v]`,
|
||||||
|
`http_requests{group="production", instance="0", job="api-server"} => 100 @[%v]`,
|
||||||
|
},
|
||||||
|
fullRanges: 0,
|
||||||
|
intervalRanges: 2,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
package metric
|
package metric
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -23,58 +24,131 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetFingerprintsForLabelSetTests(p MetricPersistence, t test.Tester) {
|
func GetFingerprintsForLabelSetTests(p MetricPersistence, t test.Tester) {
|
||||||
testAppendSamples(p, &clientmodel.Sample{
|
metrics := []clientmodel.Metric{
|
||||||
Value: 0,
|
{
|
||||||
Timestamp: 0,
|
clientmodel.MetricNameLabel: "test_metric",
|
||||||
Metric: clientmodel.Metric{
|
"method": "get",
|
||||||
clientmodel.MetricNameLabel: "my_metric",
|
"result": "success",
|
||||||
"request_type": "your_mom",
|
|
||||||
},
|
},
|
||||||
}, t)
|
{
|
||||||
|
clientmodel.MetricNameLabel: "test_metric",
|
||||||
testAppendSamples(p, &clientmodel.Sample{
|
"method": "get",
|
||||||
Value: 0,
|
"result": "failure",
|
||||||
Timestamp: 0,
|
},
|
||||||
Metric: clientmodel.Metric{
|
{
|
||||||
clientmodel.MetricNameLabel: "my_metric",
|
clientmodel.MetricNameLabel: "test_metric",
|
||||||
"request_type": "your_dad",
|
"method": "post",
|
||||||
|
"result": "success",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
clientmodel.MetricNameLabel: "test_metric",
|
||||||
|
"method": "post",
|
||||||
|
"result": "failure",
|
||||||
},
|
},
|
||||||
}, t)
|
|
||||||
|
|
||||||
result, err := p.GetFingerprintsForLabelSet(clientmodel.LabelSet{
|
|
||||||
clientmodel.MetricNameLabel: clientmodel.LabelValue("my_metric"),
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(result) != 2 {
|
newTestLabelMatcher := func(matchType MatchType, name clientmodel.LabelName, value clientmodel.LabelValue) *LabelMatcher {
|
||||||
t.Errorf("Expected two elements.")
|
m, err := NewLabelMatcher(matchType, name, value)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Couldn't create label matcher: %v", err)
|
||||||
|
}
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err = p.GetFingerprintsForLabelSet(clientmodel.LabelSet{
|
scenarios := []struct {
|
||||||
clientmodel.LabelName("request_type"): clientmodel.LabelValue("your_mom"),
|
in LabelMatchers
|
||||||
})
|
outIndexes []int
|
||||||
|
}{
|
||||||
if err != nil {
|
{
|
||||||
t.Error(err)
|
in: LabelMatchers{
|
||||||
|
newTestLabelMatcher(Equal, clientmodel.MetricNameLabel, "test_metric"),
|
||||||
|
},
|
||||||
|
outIndexes: []int{0, 1, 2, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: LabelMatchers{
|
||||||
|
newTestLabelMatcher(Equal, clientmodel.MetricNameLabel, "non_existent_metric"),
|
||||||
|
},
|
||||||
|
outIndexes: []int{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: LabelMatchers{
|
||||||
|
newTestLabelMatcher(Equal, clientmodel.MetricNameLabel, "non_existent_metric"),
|
||||||
|
newTestLabelMatcher(Equal, "result", "success"),
|
||||||
|
},
|
||||||
|
outIndexes: []int{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: LabelMatchers{
|
||||||
|
newTestLabelMatcher(Equal, clientmodel.MetricNameLabel, "test_metric"),
|
||||||
|
newTestLabelMatcher(Equal, "result", "success"),
|
||||||
|
},
|
||||||
|
outIndexes: []int{0, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: LabelMatchers{
|
||||||
|
newTestLabelMatcher(Equal, clientmodel.MetricNameLabel, "test_metric"),
|
||||||
|
newTestLabelMatcher(NotEqual, "result", "success"),
|
||||||
|
},
|
||||||
|
outIndexes: []int{1, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: LabelMatchers{
|
||||||
|
newTestLabelMatcher(Equal, clientmodel.MetricNameLabel, "test_metric"),
|
||||||
|
newTestLabelMatcher(RegexMatch, "result", "foo|success|bar"),
|
||||||
|
},
|
||||||
|
outIndexes: []int{0, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: LabelMatchers{
|
||||||
|
newTestLabelMatcher(Equal, clientmodel.MetricNameLabel, "test_metric"),
|
||||||
|
newTestLabelMatcher(RegexNoMatch, "result", "foo|success|bar"),
|
||||||
|
},
|
||||||
|
outIndexes: []int{1, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: LabelMatchers{
|
||||||
|
newTestLabelMatcher(Equal, clientmodel.MetricNameLabel, "test_metric"),
|
||||||
|
newTestLabelMatcher(RegexNoMatch, "result", "foo|success|bar"),
|
||||||
|
newTestLabelMatcher(RegexMatch, "method", "os"),
|
||||||
|
},
|
||||||
|
outIndexes: []int{3},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(result) != 1 {
|
for _, m := range metrics {
|
||||||
t.Errorf("Expected one element.")
|
testAppendSamples(p, &clientmodel.Sample{
|
||||||
|
Value: 0,
|
||||||
|
Timestamp: 0,
|
||||||
|
Metric: m,
|
||||||
|
}, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err = p.GetFingerprintsForLabelSet(clientmodel.LabelSet{
|
for i, s := range scenarios {
|
||||||
clientmodel.LabelName("request_type"): clientmodel.LabelValue("your_dad"),
|
actualFps, err := p.GetFingerprintsForLabelMatchers(s.in)
|
||||||
})
|
if err != nil {
|
||||||
|
t.Fatalf("%d. Couldn't get fingerprints for label matchers: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
expectedFps := clientmodel.Fingerprints{}
|
||||||
t.Error(err)
|
for _, i := range s.outIndexes {
|
||||||
}
|
fp := &clientmodel.Fingerprint{}
|
||||||
|
fp.LoadFromMetric(metrics[i])
|
||||||
|
expectedFps = append(expectedFps, fp)
|
||||||
|
}
|
||||||
|
|
||||||
if len(result) != 1 {
|
sort.Sort(actualFps)
|
||||||
t.Errorf("Expected one element.")
|
sort.Sort(expectedFps)
|
||||||
|
|
||||||
|
if len(actualFps) != len(expectedFps) {
|
||||||
|
t.Fatalf("%d. Got %d fingerprints; want %d", i, len(actualFps), len(expectedFps))
|
||||||
|
}
|
||||||
|
|
||||||
|
for j, actualFp := range actualFps {
|
||||||
|
if !actualFp.Equal(expectedFps[j]) {
|
||||||
|
t.Fatalf("%d.%d. Got fingerprint %v; want %v", i, j, actualFp, expectedFps[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,9 +214,11 @@ func GetMetricForFingerprintTests(p MetricPersistence, t test.Tester) {
|
||||||
},
|
},
|
||||||
}, t)
|
}, t)
|
||||||
|
|
||||||
result, err := p.GetFingerprintsForLabelSet(clientmodel.LabelSet{
|
result, err := p.GetFingerprintsForLabelMatchers(LabelMatchers{{
|
||||||
clientmodel.LabelName("request_type"): clientmodel.LabelValue("your_mom"),
|
Type: Equal,
|
||||||
})
|
Name: "request_type",
|
||||||
|
Value: "your_mom",
|
||||||
|
}})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
@ -169,9 +245,11 @@ func GetMetricForFingerprintTests(p MetricPersistence, t test.Tester) {
|
||||||
t.Errorf("Expected metric to match.")
|
t.Errorf("Expected metric to match.")
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err = p.GetFingerprintsForLabelSet(clientmodel.LabelSet{
|
result, err = p.GetFingerprintsForLabelMatchers(LabelMatchers{{
|
||||||
clientmodel.LabelName("request_type"): clientmodel.LabelValue("your_dad"),
|
Type: Equal,
|
||||||
})
|
Name: "request_type",
|
||||||
|
Value: "your_dad",
|
||||||
|
}})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
@ -256,15 +334,15 @@ func AppendRepeatingValuesTests(p MetricPersistence, t test.Tester) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
labelSet := clientmodel.LabelSet{
|
matchers := labelMatchersFromLabelSet(clientmodel.LabelSet{
|
||||||
clientmodel.MetricNameLabel: "errors_total",
|
clientmodel.MetricNameLabel: "errors_total",
|
||||||
"controller": "foo",
|
"controller": "foo",
|
||||||
"operation": "bar",
|
"operation": "bar",
|
||||||
}
|
})
|
||||||
|
|
||||||
for i := 0; i < increments; i++ {
|
for i := 0; i < increments; i++ {
|
||||||
for j := 0; j < repetitions; j++ {
|
for j := 0; j < repetitions; j++ {
|
||||||
fingerprints, err := p.GetFingerprintsForLabelSet(labelSet)
|
fingerprints, err := p.GetFingerprintsForLabelMatchers(matchers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -319,15 +397,15 @@ func AppendsRepeatingValuesTests(p MetricPersistence, t test.Tester) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
labelSet := clientmodel.LabelSet{
|
matchers := labelMatchersFromLabelSet(clientmodel.LabelSet{
|
||||||
clientmodel.MetricNameLabel: "errors_total",
|
clientmodel.MetricNameLabel: "errors_total",
|
||||||
"controller": "foo",
|
"controller": "foo",
|
||||||
"operation": "bar",
|
"operation": "bar",
|
||||||
}
|
})
|
||||||
|
|
||||||
for i := 0; i < increments; i++ {
|
for i := 0; i < increments; i++ {
|
||||||
for j := 0; j < repetitions; j++ {
|
for j := 0; j < repetitions; j++ {
|
||||||
fingerprints, err := p.GetFingerprintsForLabelSet(labelSet)
|
fingerprints, err := p.GetFingerprintsForLabelMatchers(matchers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,15 +25,15 @@ const (
|
||||||
failure = "failure"
|
failure = "failure"
|
||||||
result = "result"
|
result = "result"
|
||||||
|
|
||||||
appendSample = "append_sample"
|
appendSample = "append_sample"
|
||||||
appendSamples = "append_samples"
|
appendSamples = "append_samples"
|
||||||
flushMemory = "flush_memory"
|
flushMemory = "flush_memory"
|
||||||
getLabelValuesForLabelName = "get_label_values_for_label_name"
|
getLabelValuesForLabelName = "get_label_values_for_label_name"
|
||||||
getFingerprintsForLabelSet = "get_fingerprints_for_labelset"
|
getFingerprintsForLabelMatchers = "get_fingerprints_for_label_matchers"
|
||||||
getMetricForFingerprint = "get_metric_for_fingerprint"
|
getMetricForFingerprint = "get_metric_for_fingerprint"
|
||||||
hasIndexMetric = "has_index_metric"
|
hasIndexMetric = "has_index_metric"
|
||||||
refreshHighWatermarks = "refresh_high_watermarks"
|
refreshHighWatermarks = "refresh_high_watermarks"
|
||||||
renderView = "render_view"
|
renderView = "render_view"
|
||||||
|
|
||||||
cutOff = "recency_threshold"
|
cutOff = "recency_threshold"
|
||||||
processorName = "processor"
|
processorName = "processor"
|
||||||
|
|
|
@ -29,8 +29,8 @@ type MetricPersistence interface {
|
||||||
AppendSamples(clientmodel.Samples) error
|
AppendSamples(clientmodel.Samples) error
|
||||||
|
|
||||||
// Get all of the metric fingerprints that are associated with the
|
// Get all of the metric fingerprints that are associated with the
|
||||||
// provided label set.
|
// provided label matchers.
|
||||||
GetFingerprintsForLabelSet(clientmodel.LabelSet) (clientmodel.Fingerprints, error)
|
GetFingerprintsForLabelMatchers(LabelMatchers) (clientmodel.Fingerprints, error)
|
||||||
|
|
||||||
// Get all of the label values that are associated with a given label name.
|
// Get all of the label values that are associated with a given label name.
|
||||||
GetLabelValuesForLabelName(clientmodel.LabelName) (clientmodel.LabelValues, error)
|
GetLabelValuesForLabelName(clientmodel.LabelName) (clientmodel.LabelValues, error)
|
||||||
|
|
|
@ -52,7 +52,7 @@ type LevelDBMetricPersistence struct {
|
||||||
//
|
//
|
||||||
// type FingerprintResolver interface {
|
// type FingerprintResolver interface {
|
||||||
// GetFingerprintForMetric(clientmodel.Metric) (*clientmodel.Fingerprint, bool, error)
|
// GetFingerprintForMetric(clientmodel.Metric) (*clientmodel.Fingerprint, bool, error)
|
||||||
// GetFingerprintsForLabelSet(LabelPair) (clientmodel.Fingerprints, bool, error)
|
// GetFingerprintsForLabelMatchers(LabelPair) (clientmodel.Fingerprints, bool, error)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// type MetricResolver interface {
|
// type MetricResolver interface {
|
||||||
|
@ -419,33 +419,57 @@ func (l *LevelDBMetricPersistence) hasIndexMetric(m clientmodel.Metric) (value b
|
||||||
return l.MetricMembershipIndex.Has(m)
|
return l.MetricMembershipIndex.Has(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFingerprintsForLabelSet returns the Fingerprints for the given LabelSet by
|
// GetFingerprintsForLabelMatchers returns the Fingerprints for the given
|
||||||
// querying the underlying LabelPairFingerprintIndex for each LabelPair
|
// LabelMatchers by querying the underlying LabelPairFingerprintIndex and
|
||||||
// contained in LabelSet. It implements the MetricPersistence interface.
|
// possibly the LabelNameLabelValuesIndex for each matcher. It implements the
|
||||||
func (l *LevelDBMetricPersistence) GetFingerprintsForLabelSet(labelSet clientmodel.LabelSet) (fps clientmodel.Fingerprints, err error) {
|
// MetricPersistence interface.
|
||||||
|
func (l *LevelDBMetricPersistence) GetFingerprintsForLabelMatchers(labelMatchers LabelMatchers) (fps clientmodel.Fingerprints, err error) {
|
||||||
defer func(begin time.Time) {
|
defer func(begin time.Time) {
|
||||||
duration := time.Since(begin)
|
duration := time.Since(begin)
|
||||||
|
|
||||||
recordOutcome(duration, err, map[string]string{operation: getFingerprintsForLabelSet, result: success}, map[string]string{operation: getFingerprintsForLabelSet, result: failure})
|
recordOutcome(duration, err, map[string]string{operation: getFingerprintsForLabelMatchers, result: success}, map[string]string{operation: getFingerprintsForLabelMatchers, result: failure})
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
|
|
||||||
sets := []utility.Set{}
|
sets := []utility.Set{}
|
||||||
|
|
||||||
for name, value := range labelSet {
|
for _, matcher := range labelMatchers {
|
||||||
fps, _, err := l.LabelPairToFingerprints.Lookup(&LabelPair{
|
|
||||||
Name: name,
|
|
||||||
Value: value,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
set := utility.Set{}
|
set := utility.Set{}
|
||||||
|
|
||||||
for _, fp := range fps {
|
switch matcher.Type {
|
||||||
set.Add(*fp)
|
case Equal:
|
||||||
}
|
fps, _, err := l.LabelPairToFingerprints.Lookup(&LabelPair{
|
||||||
|
Name: matcher.Name,
|
||||||
|
Value: matcher.Value,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fp := range fps {
|
||||||
|
set.Add(*fp)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
values, err := l.GetLabelValuesForLabelName(matcher.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
matches := matcher.Filter(values)
|
||||||
|
if len(matches) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
for _, v := range matches {
|
||||||
|
fps, _, err := l.LabelPairToFingerprints.Lookup(&LabelPair{
|
||||||
|
Name: matcher.Name,
|
||||||
|
Value: v,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, fp := range fps {
|
||||||
|
set.Add(*fp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
sets = append(sets, set)
|
sets = append(sets, set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
112
storage/metric/matcher.go
Normal file
112
storage/metric/matcher.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
// Copyright 2014 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 metric
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
clientmodel "github.com/prometheus/client_golang/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MatchType is an enum for label matching types.
|
||||||
|
type MatchType int
|
||||||
|
|
||||||
|
// Possible MatchTypes.
|
||||||
|
const (
|
||||||
|
Equal MatchType = iota
|
||||||
|
NotEqual
|
||||||
|
RegexMatch
|
||||||
|
RegexNoMatch
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m MatchType) String() string {
|
||||||
|
typeToStr := map[MatchType]string{
|
||||||
|
Equal: "=",
|
||||||
|
NotEqual: "!=",
|
||||||
|
RegexMatch: "=~",
|
||||||
|
RegexNoMatch: "!~",
|
||||||
|
}
|
||||||
|
if str, ok := typeToStr[m]; ok {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
panic("unknown match type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabelMatchers is a slice of LabelMatcher objects.
|
||||||
|
type LabelMatchers []*LabelMatcher
|
||||||
|
|
||||||
|
// LabelMatcher models the matching of a label.
|
||||||
|
type LabelMatcher struct {
|
||||||
|
Type MatchType
|
||||||
|
Name clientmodel.LabelName
|
||||||
|
Value clientmodel.LabelValue
|
||||||
|
re *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLabelMatcher returns a LabelMatcher object ready to use.
|
||||||
|
func NewLabelMatcher(matchType MatchType, name clientmodel.LabelName, value clientmodel.LabelValue) (*LabelMatcher, error) {
|
||||||
|
m := &LabelMatcher{
|
||||||
|
Type: matchType,
|
||||||
|
Name: name,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
if matchType == RegexMatch || matchType == RegexNoMatch {
|
||||||
|
re, err := regexp.Compile(string(value))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m.re = re
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match returns true if the label matcher matches the supplied label value.
|
||||||
|
func (m *LabelMatcher) Match(v clientmodel.LabelValue) bool {
|
||||||
|
switch m.Type {
|
||||||
|
case Equal:
|
||||||
|
return m.Value == v
|
||||||
|
case NotEqual:
|
||||||
|
return m.Value != v
|
||||||
|
case RegexMatch:
|
||||||
|
return m.re.MatchString(string(v))
|
||||||
|
case RegexNoMatch:
|
||||||
|
return !m.re.MatchString(string(v))
|
||||||
|
default:
|
||||||
|
panic("invalid match type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter takes a list of label values and returns all label values which match
|
||||||
|
// the label matcher.
|
||||||
|
func (m *LabelMatcher) Filter(in clientmodel.LabelValues) clientmodel.LabelValues {
|
||||||
|
out := clientmodel.LabelValues{}
|
||||||
|
for _, v := range in {
|
||||||
|
if m.Match(v) {
|
||||||
|
out = append(out, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func labelMatchersFromLabelSet(l clientmodel.LabelSet) LabelMatchers {
|
||||||
|
m := make(LabelMatchers, 0, len(l))
|
||||||
|
for k, v := range l {
|
||||||
|
m = append(m, &LabelMatcher{
|
||||||
|
Type: Equal,
|
||||||
|
Name: k,
|
||||||
|
Value: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
|
@ -349,20 +349,45 @@ func (s *memorySeriesStorage) appendSamplesWithoutIndexing(fingerprint *clientmo
|
||||||
series.add(samples)
|
series.add(samples)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *memorySeriesStorage) GetFingerprintsForLabelSet(l clientmodel.LabelSet) (clientmodel.Fingerprints, error) {
|
func (s *memorySeriesStorage) GetFingerprintsForLabelMatchers(labelMatchers LabelMatchers) (clientmodel.Fingerprints, error) {
|
||||||
s.RLock()
|
s.RLock()
|
||||||
defer s.RUnlock()
|
defer s.RUnlock()
|
||||||
|
|
||||||
sets := []utility.Set{}
|
sets := []utility.Set{}
|
||||||
for k, v := range l {
|
for _, matcher := range labelMatchers {
|
||||||
set, ok := s.labelPairToFingerprints[LabelPair{
|
switch matcher.Type {
|
||||||
Name: k,
|
case Equal:
|
||||||
Value: v,
|
set, ok := s.labelPairToFingerprints[LabelPair{
|
||||||
}]
|
Name: matcher.Name,
|
||||||
if !ok {
|
Value: matcher.Value,
|
||||||
return nil, nil
|
}]
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
sets = append(sets, set)
|
||||||
|
default:
|
||||||
|
values, err := s.GetLabelValuesForLabelName(matcher.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
matches := matcher.Filter(values)
|
||||||
|
if len(matches) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
set := utility.Set{}
|
||||||
|
for _, v := range matches {
|
||||||
|
subset, ok := s.labelPairToFingerprints[LabelPair{
|
||||||
|
Name: matcher.Name,
|
||||||
|
Value: v,
|
||||||
|
}]
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
set = set.Union(subset)
|
||||||
|
}
|
||||||
|
sets = append(sets, set)
|
||||||
}
|
}
|
||||||
sets = append(sets, set)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setCount := len(sets)
|
setCount := len(sets)
|
||||||
|
|
|
@ -126,7 +126,7 @@ func TestDroppedSeriesIndexRegression(t *testing.T) {
|
||||||
s.AppendSamples(samples)
|
s.AppendSamples(samples)
|
||||||
|
|
||||||
common := clientmodel.LabelSet{"common": "samevalue"}
|
common := clientmodel.LabelSet{"common": "samevalue"}
|
||||||
fps, err := s.GetFingerprintsForLabelSet(common)
|
fps, err := s.GetFingerprintsForLabelMatchers(labelMatchersFromLabelSet(common))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ func TestDroppedSeriesIndexRegression(t *testing.T) {
|
||||||
t.Fatalf("Got %d disk samples, expected 1", len(diskSamples))
|
t.Fatalf("Got %d disk samples, expected 1", len(diskSamples))
|
||||||
}
|
}
|
||||||
|
|
||||||
fps, err = s.GetFingerprintsForLabelSet(common)
|
fps, err = s.GetFingerprintsForLabelMatchers(labelMatchersFromLabelSet(common))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ func GetFingerprintsForLabelSetUsesAndForLabelMatchingTests(p MetricPersistence,
|
||||||
"percentile": "0.010000",
|
"percentile": "0.010000",
|
||||||
}
|
}
|
||||||
|
|
||||||
fingerprints, err := p.GetFingerprintsForLabelSet(labelSet)
|
fingerprints, err := p.GetFingerprintsForLabelMatchers(labelMatchersFromLabelSet(labelSet))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get labels: %s", err)
|
t.Errorf("could not get labels: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,14 +40,11 @@ func BasicLifecycleTests(p MetricPersistence, t test.Tester) {
|
||||||
|
|
||||||
func ReadEmptyTests(p MetricPersistence, t test.Tester) {
|
func ReadEmptyTests(p MetricPersistence, t test.Tester) {
|
||||||
hasLabelPair := func(x int) (success bool) {
|
hasLabelPair := func(x int) (success bool) {
|
||||||
name := clientmodel.LabelName(string(x))
|
fingerprints, err := p.GetFingerprintsForLabelMatchers(LabelMatchers{{
|
||||||
value := clientmodel.LabelValue(string(x))
|
Type: Equal,
|
||||||
|
Name: clientmodel.LabelName(string(x)),
|
||||||
labelSet := clientmodel.LabelSet{
|
Value: clientmodel.LabelValue(string(x)),
|
||||||
name: value,
|
}})
|
||||||
}
|
|
||||||
|
|
||||||
fingerprints, err := p.GetFingerprintsForLabelSet(labelSet)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -150,9 +147,11 @@ func AppendSampleAsSparseAppendWithReadsTests(p MetricPersistence, t test.Tester
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fingerprints, err := p.GetFingerprintsForLabelSet(clientmodel.LabelSet{
|
fingerprints, err := p.GetFingerprintsForLabelMatchers(LabelMatchers{{
|
||||||
labelName: labelValue,
|
Type: Equal,
|
||||||
})
|
Name: labelName,
|
||||||
|
Value: labelValue,
|
||||||
|
}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -329,11 +328,13 @@ func StochasticTests(persistenceMaker func() (MetricPersistence, test.Closer), t
|
||||||
metricNewestSample[metricIndex] = newestSample
|
metricNewestSample[metricIndex] = newestSample
|
||||||
|
|
||||||
for sharedLabelIndex := 0; sharedLabelIndex < numberOfSharedLabels; sharedLabelIndex++ {
|
for sharedLabelIndex := 0; sharedLabelIndex < numberOfSharedLabels; sharedLabelIndex++ {
|
||||||
labelPair := clientmodel.LabelSet{
|
matchers := LabelMatchers{{
|
||||||
clientmodel.LabelName(fmt.Sprintf("shared_label_%d", sharedLabelIndex)): clientmodel.LabelValue(fmt.Sprintf("label_%d", sharedLabelIndex)),
|
Type: Equal,
|
||||||
}
|
Name: clientmodel.LabelName(fmt.Sprintf("shared_label_%d", sharedLabelIndex)),
|
||||||
|
Value: clientmodel.LabelValue(fmt.Sprintf("label_%d", sharedLabelIndex)),
|
||||||
|
}}
|
||||||
|
|
||||||
fingerprints, err := p.GetFingerprintsForLabelSet(labelPair)
|
fingerprints, err := p.GetFingerprintsForLabelMatchers(matchers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -349,11 +350,13 @@ func StochasticTests(persistenceMaker func() (MetricPersistence, test.Closer), t
|
||||||
for unsharedLabelIndex := 0; unsharedLabelIndex < numberOfUnsharedLabels; unsharedLabelIndex++ {
|
for unsharedLabelIndex := 0; unsharedLabelIndex < numberOfUnsharedLabels; unsharedLabelIndex++ {
|
||||||
labelName := clientmodel.LabelName(fmt.Sprintf("metric_index_%d_private_label_%d", metricIndex, unsharedLabelIndex))
|
labelName := clientmodel.LabelName(fmt.Sprintf("metric_index_%d_private_label_%d", metricIndex, unsharedLabelIndex))
|
||||||
labelValue := clientmodel.LabelValue(fmt.Sprintf("private_label_%d", unsharedLabelIndex))
|
labelValue := clientmodel.LabelValue(fmt.Sprintf("private_label_%d", unsharedLabelIndex))
|
||||||
labelSet := clientmodel.LabelSet{
|
matchers := LabelMatchers{{
|
||||||
labelName: labelValue,
|
Type: Equal,
|
||||||
}
|
Name: labelName,
|
||||||
|
Value: labelValue,
|
||||||
|
}}
|
||||||
|
|
||||||
fingerprints, err := p.GetFingerprintsForLabelSet(labelSet)
|
fingerprints, err := p.GetFingerprintsForLabelMatchers(matchers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -673,9 +673,9 @@ func (t *TieredStorage) GetAllValuesForLabel(labelName clientmodel.LabelName) (c
|
||||||
return values, nil
|
return values, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFingerprintsForLabelSet gets all of the metric fingerprints that are
|
// GetFingerprintsForLabelMatchers gets all of the metric fingerprints that are
|
||||||
// associated with the provided label set.
|
// associated with the provided label matchers.
|
||||||
func (t *TieredStorage) GetFingerprintsForLabelSet(labelSet clientmodel.LabelSet) (clientmodel.Fingerprints, error) {
|
func (t *TieredStorage) GetFingerprintsForLabelMatchers(matchers LabelMatchers) (clientmodel.Fingerprints, error) {
|
||||||
t.mu.RLock()
|
t.mu.RLock()
|
||||||
defer t.mu.RUnlock()
|
defer t.mu.RUnlock()
|
||||||
|
|
||||||
|
@ -683,11 +683,11 @@ func (t *TieredStorage) GetFingerprintsForLabelSet(labelSet clientmodel.LabelSet
|
||||||
panic("Illegal State: Attempted to query non-running TieredStorage.")
|
panic("Illegal State: Attempted to query non-running TieredStorage.")
|
||||||
}
|
}
|
||||||
|
|
||||||
memFingerprints, err := t.memoryArena.GetFingerprintsForLabelSet(labelSet)
|
memFingerprints, err := t.memoryArena.GetFingerprintsForLabelMatchers(matchers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
diskFingerprints, err := t.DiskStorage.GetFingerprintsForLabelSet(labelSet)
|
diskFingerprints, err := t.DiskStorage.GetFingerprintsForLabelMatchers(matchers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -803,7 +803,7 @@ func TestGetAllValuesForLabel(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetFingerprintsForLabelSet(t *testing.T) {
|
func TestGetFingerprintsForLabelMatchers(t *testing.T) {
|
||||||
tiered, closer := NewTestTieredStorage(t)
|
tiered, closer := NewTestTieredStorage(t)
|
||||||
defer closer.Close()
|
defer closer.Close()
|
||||||
memorySample := &clientmodel.Sample{
|
memorySample := &clientmodel.Sample{
|
||||||
|
@ -821,40 +821,65 @@ func TestGetFingerprintsForLabelSet(t *testing.T) {
|
||||||
tiered.Flush()
|
tiered.Flush()
|
||||||
|
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
labels clientmodel.LabelSet
|
matchers LabelMatchers
|
||||||
fpCount int
|
fpCount int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
labels: clientmodel.LabelSet{},
|
matchers: LabelMatchers{},
|
||||||
fpCount: 0,
|
fpCount: 0,
|
||||||
}, {
|
}, {
|
||||||
labels: clientmodel.LabelSet{
|
matchers: LabelMatchers{
|
||||||
clientmodel.MetricNameLabel: "http_requests",
|
{
|
||||||
|
Type: Equal,
|
||||||
|
Name: clientmodel.MetricNameLabel,
|
||||||
|
Value: "http_requests",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
fpCount: 2,
|
fpCount: 2,
|
||||||
}, {
|
}, {
|
||||||
labels: clientmodel.LabelSet{
|
matchers: LabelMatchers{
|
||||||
clientmodel.MetricNameLabel: "http_requests",
|
{
|
||||||
"method": "/foo",
|
Type: Equal,
|
||||||
|
Name: clientmodel.MetricNameLabel,
|
||||||
|
Value: "http_requests",
|
||||||
|
}, {
|
||||||
|
Type: Equal,
|
||||||
|
Name: "method",
|
||||||
|
Value: "/foo",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
fpCount: 1,
|
fpCount: 1,
|
||||||
}, {
|
}, {
|
||||||
labels: clientmodel.LabelSet{
|
matchers: LabelMatchers{
|
||||||
clientmodel.MetricNameLabel: "http_requests",
|
{
|
||||||
"method": "/bar",
|
Type: Equal,
|
||||||
|
Name: clientmodel.MetricNameLabel,
|
||||||
|
Value: "http_requests",
|
||||||
|
}, {
|
||||||
|
Type: Equal,
|
||||||
|
Name: "method",
|
||||||
|
Value: "/bar",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
fpCount: 1,
|
fpCount: 1,
|
||||||
}, {
|
}, {
|
||||||
labels: clientmodel.LabelSet{
|
matchers: LabelMatchers{
|
||||||
clientmodel.MetricNameLabel: "http_requests",
|
{
|
||||||
"method": "/baz",
|
Type: Equal,
|
||||||
|
Name: clientmodel.MetricNameLabel,
|
||||||
|
Value: "http_requests",
|
||||||
|
}, {
|
||||||
|
Type: Equal,
|
||||||
|
Name: "method",
|
||||||
|
Value: "/baz",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
fpCount: 0,
|
fpCount: 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, scenario := range scenarios {
|
for i, scenario := range scenarios {
|
||||||
fingerprints, err := tiered.GetFingerprintsForLabelSet(scenario.labels)
|
fingerprints, err := tiered.GetFingerprintsForLabelMatchers(scenario.matchers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%d. Error getting metric names: %s", i, err)
|
t.Fatalf("%d. Error getting metric names: %s", i, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,16 +13,20 @@
|
||||||
|
|
||||||
package utility
|
package utility
|
||||||
|
|
||||||
type Set map[interface{}]bool
|
// Set is a type which models a set.
|
||||||
|
type Set map[interface{}]struct{}
|
||||||
|
|
||||||
|
// Add adds an item to the set.
|
||||||
func (s Set) Add(v interface{}) {
|
func (s Set) Add(v interface{}) {
|
||||||
s[v] = true
|
s[v] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove removes an item from the set.
|
||||||
func (s Set) Remove(v interface{}) {
|
func (s Set) Remove(v interface{}) {
|
||||||
delete(s, v)
|
delete(s, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Elements returns a slice containing all elements in the set.
|
||||||
func (s Set) Elements() []interface{} {
|
func (s Set) Elements() []interface{} {
|
||||||
result := make([]interface{}, 0, len(s))
|
result := make([]interface{}, 0, len(s))
|
||||||
|
|
||||||
|
@ -33,20 +37,36 @@ func (s Set) Elements() []interface{} {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Has returns true if an element is contained in the set.
|
||||||
func (s Set) Has(v interface{}) bool {
|
func (s Set) Has(v interface{}) bool {
|
||||||
_, p := s[v]
|
_, p := s[v]
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Intersection returns a new set with items that exist in both sets.
|
||||||
func (s Set) Intersection(o Set) Set {
|
func (s Set) Intersection(o Set) Set {
|
||||||
result := make(Set)
|
result := Set{}
|
||||||
|
|
||||||
for k := range s {
|
for k := range s {
|
||||||
if o.Has(k) {
|
if o.Has(k) {
|
||||||
result[k] = true
|
result[k] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Union returns a new set with all items in both sets.
|
||||||
|
func (s Set) Union(o Set) Set {
|
||||||
|
result := Set{}
|
||||||
|
|
||||||
|
for k := range s {
|
||||||
|
result[k] = struct{}{}
|
||||||
|
}
|
||||||
|
for k := range o {
|
||||||
|
result[k] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue