diff --git a/model/textparse/interface.go b/model/textparse/interface.go index 61acb8a066..2dcf4fb4a8 100644 --- a/model/textparse/interface.go +++ b/model/textparse/interface.go @@ -51,8 +51,8 @@ type Parser interface { // The returned byte slices become invalid after the next call to Next. Unit() ([]byte, []byte) - // Identifiers returns the identifiers for info metrics - Identifiers() []string + // IdentifyingLabels returns the identifying labels for info metrics + IdentifyingLabels() []string // Comment returns the text of the current comment. // Must only be called after Next returned a comment entry. @@ -113,5 +113,4 @@ const ( EntryComment Entry = 3 EntryUnit Entry = 4 EntryHistogram Entry = 5 // A series with a native histogram as a value. - EntryIdent Entry = 6 ) diff --git a/model/textparse/openmetricslex.l b/model/textparse/openmetricslex.l index 442455871b..b0b0350ab0 100644 --- a/model/textparse/openmetricslex.l +++ b/model/textparse/openmetricslex.l @@ -33,10 +33,10 @@ func (l *openMetricsLexer) Lex() token { D [0-9] L [a-zA-Z_] M [a-zA-Z_:] -C [^\n] +C [^\n()] S [ ] -%x sComment sMeta1 sMeta2 sLabels sLValue sValue sTimestamp sExemplar sEValue sETimestamp sMeta3 +%x sComment sMeta1 sMeta2 sLabels sLValue sValue sTimestamp sExemplar sEValue sETimestamp sIdens %yyc c %yyn c = l.next() @@ -50,13 +50,14 @@ S [ ] TYPE{S} l.state = sMeta1; return tType UNIT{S} l.state = sMeta1; return tUnit "EOF"\n? l.state = sInit; return tEOFWord -ID{S} l.state = sMeta3; return tIdens \"(\\.|[^\\"])*\" l.state = sMeta2; return tMName {M}({M}|{D})* l.state = sMeta2; return tMName -{S}{C}*\n l.state = sInit; return tText -{L}* return tLName -, return tComma -\n l.state = sInit; return tLinebreak +{S}{C}* l.state = sMeta2; return tText +\( l.state = sIdens; return tParentOpen +{L}({L}|{D})* return tLName +, return tComma +\) l.state = sMeta2; return tParentClose +\n l.state = sInit; return tLinebreak {M}({M}|{D})* l.state = sValue; return tMName \{ l.state = sLabels; return tBraceOpen diff --git a/model/textparse/openmetricslex.l.go b/model/textparse/openmetricslex.l.go index a0cb8b7cdf..333d2c80fd 100644 --- a/model/textparse/openmetricslex.l.go +++ b/model/textparse/openmetricslex.l.go @@ -39,25 +39,25 @@ yystate0: case 1: // start condition: sComment goto yystart6 case 2: // start condition: sMeta1 - goto yystart29 + goto yystart26 case 3: // start condition: sMeta2 - goto yystart34 + goto yystart31 case 4: // start condition: sLabels - goto yystart37 + goto yystart35 case 5: // start condition: sLValue - goto yystart45 + goto yystart43 case 6: // start condition: sValue - goto yystart49 + goto yystart47 case 7: // start condition: sTimestamp - goto yystart53 + goto yystart51 case 8: // start condition: sExemplar - goto yystart60 + goto yystart58 case 9: // start condition: sEValue - goto yystart65 + goto yystart63 case 10: // start condition: sETimestamp - goto yystart71 - case 11: // start condition: sMeta3 - goto yystart75 + goto yystart69 + case 11: // start condition: sIdens + goto yystart73 } yystate1: @@ -91,14 +91,14 @@ yystate4: c = l.next() switch { default: - goto yyrule13 + goto yyrule14 case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate4 } yystate5: c = l.next() - goto yyrule15 + goto yyrule16 yystate6: c = l.next() @@ -110,12 +110,10 @@ yystart6: goto yystate7 case c == 'H': goto yystate11 - case c == 'I': - goto yystate16 case c == 'T': - goto yystate19 + goto yystate16 case c == 'U': - goto yystate24 + goto yystate21 } yystate7: @@ -194,7 +192,7 @@ yystate16: switch { default: goto yyabort - case c == 'D': + case c == 'Y': goto yystate17 } @@ -203,517 +201,501 @@ yystate17: switch { default: goto yyabort - case c == ' ': + case c == 'P': goto yystate18 } yystate18: - c = l.next() - goto yyrule6 - -yystate19: - c = l.next() - switch { - default: - goto yyabort - case c == 'Y': - goto yystate20 - } - -yystate20: - c = l.next() - switch { - default: - goto yyabort - case c == 'P': - goto yystate21 - } - -yystate21: c = l.next() switch { default: goto yyabort case c == 'E': - goto yystate22 + goto yystate19 } -yystate22: +yystate19: c = l.next() switch { default: goto yyabort case c == ' ': - goto yystate23 + goto yystate20 } -yystate23: +yystate20: c = l.next() goto yyrule3 -yystate24: +yystate21: c = l.next() switch { default: goto yyabort case c == 'N': - goto yystate25 + goto yystate22 } -yystate25: +yystate22: c = l.next() switch { default: goto yyabort case c == 'I': - goto yystate26 + goto yystate23 } -yystate26: +yystate23: c = l.next() switch { default: goto yyabort case c == 'T': - goto yystate27 + goto yystate24 } -yystate27: +yystate24: c = l.next() switch { default: goto yyabort case c == ' ': - goto yystate28 + goto yystate25 } -yystate28: +yystate25: c = l.next() goto yyrule4 -yystate29: +yystate26: c = l.next() -yystart29: +yystart26: switch { default: goto yyabort case c == '"': - goto yystate30 + goto yystate27 case c == ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': - goto yystate33 + goto yystate30 } -yystate30: +yystate27: c = l.next() switch { default: goto yyabort case c == '"': - goto yystate31 + goto yystate28 case c == '\\': - goto yystate32 + goto yystate29 case c >= '\x01' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ': - goto yystate30 + goto yystate27 } -yystate31: +yystate28: c = l.next() - goto yyrule7 + goto yyrule6 -yystate32: +yystate29: c = l.next() switch { default: goto yyabort case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ': - goto yystate30 + goto yystate27 } -yystate33: +yystate30: c = l.next() switch { default: - goto yyrule8 + goto yyrule7 case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': - goto yystate33 + goto yystate30 } -yystate34: +yystate31: c = l.next() -yystart34: +yystart31: switch { default: goto yyabort case c == ' ': - goto yystate35 + goto yystate33 + case c == '(': + goto yystate34 + case c == '\n': + goto yystate32 } -yystate35: +yystate32: + c = l.next() + goto yyrule13 + +yystate33: c = l.next() switch { default: - goto yyabort - case c == '\n': - goto yystate36 - case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ': - goto yystate35 + goto yyrule8 + case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\'' || c >= '*' && c <= 'ÿ': + goto yystate33 } -yystate36: +yystate34: c = l.next() goto yyrule9 -yystate37: +yystate35: c = l.next() -yystart37: +yystart35: switch { default: goto yyabort case c == '"': - goto yystate38 + goto yystate36 case c == ',': - goto yystate41 + goto yystate39 case c == '=': - goto yystate42 + goto yystate40 case c == '}': - goto yystate44 + goto yystate42 case c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': - goto yystate43 + goto yystate41 } -yystate38: +yystate36: c = l.next() switch { default: goto yyabort case c == '"': - goto yystate39 + goto yystate37 case c == '\\': - goto yystate40 - case c >= '\x01' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ': goto yystate38 + case c >= '\x01' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ': + goto yystate36 } -yystate39: +yystate37: c = l.next() - goto yyrule17 + goto yyrule18 -yystate40: +yystate38: c = l.next() switch { default: goto yyabort case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ': - goto yystate38 + goto yystate36 } -yystate41: +yystate39: c = l.next() - goto yyrule20 + goto yyrule21 -yystate42: +yystate40: c = l.next() - goto yyrule19 + goto yyrule20 -yystate43: +yystate41: c = l.next() switch { default: - goto yyrule16 + goto yyrule17 case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': - goto yystate43 + goto yystate41 } -yystate44: +yystate42: c = l.next() - goto yyrule18 + goto yyrule19 -yystate45: +yystate43: c = l.next() -yystart45: +yystart43: switch { default: goto yyabort case c == '"': - goto yystate46 + goto yystate44 } -yystate46: +yystate44: c = l.next() switch { default: goto yyabort case c == '"': - goto yystate47 + goto yystate45 case c == '\\': - goto yystate48 - case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ': goto yystate46 + case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ': + goto yystate44 } -yystate47: +yystate45: c = l.next() - goto yyrule21 + goto yyrule22 -yystate48: +yystate46: c = l.next() switch { default: goto yyabort case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ': - goto yystate46 + goto yystate44 } -yystate49: +yystate47: c = l.next() -yystart49: +yystart47: switch { default: goto yyabort case c == ' ': - goto yystate50 + goto yystate48 case c == '{': - goto yystate52 + goto yystate50 } -yystate50: +yystate48: c = l.next() switch { default: goto yyabort case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate51 + goto yystate49 } -yystate51: +yystate49: c = l.next() switch { default: - goto yyrule22 + goto yyrule23 case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate51 + goto yystate49 } -yystate52: +yystate50: c = l.next() - goto yyrule14 + goto yyrule15 -yystate53: +yystate51: c = l.next() -yystart53: +yystart51: switch { default: goto yyabort case c == ' ': - goto yystate55 + goto yystate53 case c == '\n': - goto yystate54 + goto yystate52 } -yystate54: +yystate52: c = l.next() - goto yyrule24 + goto yyrule25 -yystate55: +yystate53: c = l.next() switch { default: goto yyabort case c == '#': - goto yystate57 + goto yystate55 case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c == '!' || c == '"' || c >= '$' && c <= 'ÿ': - goto yystate56 + goto yystate54 } -yystate56: +yystate54: c = l.next() switch { default: - goto yyrule23 + goto yyrule24 case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate56 + goto yystate54 } -yystate57: +yystate55: c = l.next() switch { default: - goto yyrule23 + goto yyrule24 case c == ' ': - goto yystate58 - case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': goto yystate56 + case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': + goto yystate54 } -yystate58: +yystate56: c = l.next() switch { default: goto yyabort case c == '{': - goto yystate59 + goto yystate57 } -yystate59: +yystate57: c = l.next() - goto yyrule25 + goto yyrule26 -yystate60: +yystate58: c = l.next() -yystart60: +yystart58: switch { default: goto yyabort case c == ',': - goto yystate61 + goto yystate59 case c == '=': - goto yystate62 + goto yystate60 case c == '}': - goto yystate64 + goto yystate62 case c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': - goto yystate63 + goto yystate61 } -yystate61: +yystate59: c = l.next() - goto yyrule30 + goto yyrule31 -yystate62: +yystate60: c = l.next() - goto yyrule28 + goto yyrule29 -yystate63: +yystate61: c = l.next() switch { default: - goto yyrule26 + goto yyrule27 case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': - goto yystate63 + goto yystate61 } -yystate64: +yystate62: c = l.next() - goto yyrule27 + goto yyrule28 -yystate65: +yystate63: c = l.next() -yystart65: +yystart63: switch { default: goto yyabort case c == ' ': - goto yystate66 + goto yystate64 case c == '"': - goto yystate68 + goto yystate66 } -yystate66: +yystate64: c = l.next() switch { default: goto yyabort case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate67 + goto yystate65 } -yystate67: +yystate65: c = l.next() switch { default: - goto yyrule31 + goto yyrule32 case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate67 + goto yystate65 } -yystate68: +yystate66: c = l.next() switch { default: goto yyabort case c == '"': - goto yystate69 + goto yystate67 case c == '\\': - goto yystate70 - case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ': goto yystate68 + case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ': + goto yystate66 } -yystate69: +yystate67: c = l.next() - goto yyrule29 + goto yyrule30 -yystate70: +yystate68: c = l.next() switch { default: goto yyabort case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ': - goto yystate68 + goto yystate66 } -yystate71: +yystate69: c = l.next() -yystart71: +yystart69: switch { default: goto yyabort case c == ' ': - goto yystate73 + goto yystate71 case c == '\n': - goto yystate72 + goto yystate70 } -yystate72: +yystate70: c = l.next() - goto yyrule33 + goto yyrule34 -yystate73: +yystate71: c = l.next() switch { default: goto yyabort case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate74 + goto yystate72 } -yystate74: +yystate72: c = l.next() switch { default: - goto yyrule32 + goto yyrule33 case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate74 + goto yystate72 } -yystate75: +yystate73: c = l.next() -yystart75: +yystart73: switch { default: - goto yyrule10 + goto yyabort + case c == ')': + goto yystate74 case c == ',': - goto yystate77 - case c == '\n': - goto yystate76 + goto yystate75 case c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': - goto yystate78 + goto yystate76 } -yystate76: +yystate74: c = l.next() goto yyrule12 -yystate77: +yystate75: c = l.next() goto yyrule11 -yystate78: +yystate76: c = l.next() switch { default: goto yyrule10 - case c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': - goto yystate78 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate76 } yyrule1: // #{S} @@ -745,31 +727,31 @@ yyrule5: // "EOF"\n? return tEOFWord goto yystate0 } -yyrule6: // ID{S} +yyrule6: // \"(\\.|[^\\"])*\" { - l.state = sMeta3 - return tIdens + l.state = sMeta2 + return tMName goto yystate0 } -yyrule7: // \"(\\.|[^\\"])*\" +yyrule7: // {M}({M}|{D})* { l.state = sMeta2 return tMName goto yystate0 } -yyrule8: // {M}({M}|{D})* +yyrule8: // {S}{C}* { l.state = sMeta2 - return tMName + return tText goto yystate0 } -yyrule9: // {S}{C}*\n +yyrule9: // \( { - l.state = sInit - return tText + l.state = sIdens + return tParentOpen goto yystate0 } -yyrule10: // {L}* +yyrule10: // {L}({L}|{D})* { return tLName } @@ -777,121 +759,127 @@ yyrule11: // , { return tComma } -yyrule12: // \n +yyrule12: // \) + { + l.state = sMeta2 + return tParentClose + goto yystate0 + } +yyrule13: // \n { l.state = sInit return tLinebreak goto yystate0 } -yyrule13: // {M}({M}|{D})* +yyrule14: // {M}({M}|{D})* { l.state = sValue return tMName goto yystate0 } -yyrule14: // \{ +yyrule15: // \{ { l.state = sLabels return tBraceOpen goto yystate0 } -yyrule15: // \{ +yyrule16: // \{ { l.state = sLabels return tBraceOpen goto yystate0 } -yyrule16: // {L}({L}|{D})* +yyrule17: // {L}({L}|{D})* { return tLName } -yyrule17: // \"(\\.|[^\\"])*\" +yyrule18: // \"(\\.|[^\\"])*\" { l.state = sLabels return tQString goto yystate0 } -yyrule18: // \} +yyrule19: // \} { l.state = sValue return tBraceClose goto yystate0 } -yyrule19: // = +yyrule20: // = { l.state = sLValue return tEqual goto yystate0 } -yyrule20: // , +yyrule21: // , { return tComma } -yyrule21: // \"(\\.|[^\\"\n])*\" +yyrule22: // \"(\\.|[^\\"\n])*\" { l.state = sLabels return tLValue goto yystate0 } -yyrule22: // {S}[^ \n]+ +yyrule23: // {S}[^ \n]+ { l.state = sTimestamp return tValue goto yystate0 } -yyrule23: // {S}[^ \n]+ +yyrule24: // {S}[^ \n]+ { return tTimestamp } -yyrule24: // \n +yyrule25: // \n { l.state = sInit return tLinebreak goto yystate0 } -yyrule25: // {S}#{S}\{ +yyrule26: // {S}#{S}\{ { l.state = sExemplar return tComment goto yystate0 } -yyrule26: // {L}({L}|{D})* +yyrule27: // {L}({L}|{D})* { return tLName } -yyrule27: // \} +yyrule28: // \} { l.state = sEValue return tBraceClose goto yystate0 } -yyrule28: // = +yyrule29: // = { l.state = sEValue return tEqual goto yystate0 } -yyrule29: // \"(\\.|[^\\"\n])*\" +yyrule30: // \"(\\.|[^\\"\n])*\" { l.state = sExemplar return tLValue goto yystate0 } -yyrule30: // , +yyrule31: // , { return tComma } -yyrule31: // {S}[^ \n]+ +yyrule32: // {S}[^ \n]+ { l.state = sETimestamp return tValue goto yystate0 } -yyrule32: // {S}[^ \n]+ +yyrule33: // {S}[^ \n]+ { return tTimestamp } -yyrule33: // \n +yyrule34: // \n if true { // avoid go vet determining the below panic will not be reached l.state = sInit return tLinebreak @@ -915,34 +903,34 @@ yyabort: // no lexem recognized goto yystate6 } if false { - goto yystate29 + goto yystate26 } if false { - goto yystate34 + goto yystate31 } if false { - goto yystate37 + goto yystate35 } if false { - goto yystate45 + goto yystate43 } if false { - goto yystate49 + goto yystate47 } if false { - goto yystate53 + goto yystate51 } if false { - goto yystate60 + goto yystate58 } if false { - goto yystate65 + goto yystate63 } if false { - goto yystate71 + goto yystate69 } if false { - goto yystate75 + goto yystate73 } } diff --git a/model/textparse/openmetricsparse.go b/model/textparse/openmetricsparse.go index db3c3039a3..8a26b1f78f 100644 --- a/model/textparse/openmetricsparse.go +++ b/model/textparse/openmetricsparse.go @@ -90,12 +90,12 @@ type OpenMetricsParser struct { // of the label name and value start and end characters. offsets []int - identifierlbs []string - eOffsets []int - exemplar []byte - exemplarVal float64 - exemplarTs int64 - hasExemplarTs bool + identifyingLabels []string + eOffsets []int + exemplar []byte + exemplarVal float64 + exemplarTs int64 + hasExemplarTs bool } // NewOpenMetricsParser returns a new parser of the byte slice. @@ -143,8 +143,8 @@ func (p *OpenMetricsParser) Type() ([]byte, model.MetricType) { return p.l.b[p.offsets[0]:p.offsets[1]], p.mtype } -func (p *OpenMetricsParser) Identifiers() []string { - return p.identifierlbs +func (p *OpenMetricsParser) IdentifyingLabels() []string { + return p.identifyingLabels } // Unit returns the metric name and unit in the current entry. @@ -252,13 +252,15 @@ func (p *OpenMetricsParser) Next() (Entry, error) { p.start = p.l.i p.offsets = p.offsets[:0] - p.identifierlbs = p.identifierlbs[:0] + p.identifyingLabels = p.identifyingLabels[:0] p.eOffsets = p.eOffsets[:0] p.exemplar = p.exemplar[:0] p.exemplarVal = 0 p.hasExemplarTs = false - switch t := p.nextToken(); t { + t := p.nextToken() + + switch t { case tEOFWord: if t := p.nextToken(); t != tEOF { return EntryInvalid, errors.New("unexpected data after # EOF") @@ -266,12 +268,6 @@ func (p *OpenMetricsParser) Next() (Entry, error) { return EntryInvalid, io.EOF case tEOF: return EntryInvalid, errors.New("data does not end with # EOF") - case tIdens: - p.identifierlbs, err = p.parseIdentifierLabels() - if err != nil { - return EntryInvalid, err - } - return EntryIdent, nil case tHelp, tType, tUnit: switch t2 := p.nextToken(); t2 { case tMName: @@ -287,11 +283,21 @@ func (p *OpenMetricsParser) Next() (Entry, error) { } switch t2 := p.nextToken(); t2 { case tText: + fmt.Println(string(p.l.b[p.l.start:p.l.i])) if len(p.l.buf()) > 1 { - p.text = p.l.buf()[1 : len(p.l.buf())-1] + p.text = p.l.buf()[1:len(p.l.buf())] } else { p.text = []byte{} } + t3 := p.nextToken() + if t == tType && t3 == tParentOpen { + if p.identifyingLabels, err = p.parseIdentifierLabels(); err != nil { + return EntryInvalid, err + } + } else if t3 != tLinebreak { + return EntryInvalid, p.parseError("expected linebreak get", t3) + } + default: return EntryInvalid, fmt.Errorf("expected text in %s", t.String()) } @@ -317,6 +323,7 @@ func (p *OpenMetricsParser) Next() (Entry, error) { default: return EntryInvalid, fmt.Errorf("invalid metric type %q", s) } + case tHelp: if !utf8.Valid(p.text) { return EntryInvalid, fmt.Errorf("help text %q is not a valid utf8 string", p.text) @@ -417,8 +424,16 @@ func (p *OpenMetricsParser) parseComment() error { } func (p *OpenMetricsParser) parseIdentifierLabels() ([]string, error) { - res := p.identifierlbs + res := p.identifyingLabels t := p.nextToken() + // for the case when there is an empty () + if t == tParentClose { + if l := p.nextToken(); l != tLinebreak { + return nil, p.parseError("expected linebreak after )", l) + } + return nil, nil + } + for { curTStart := p.l.start curTI := p.l.i @@ -427,7 +442,7 @@ func (p *OpenMetricsParser) parseIdentifierLabels() ([]string, error) { case tLName: res = append(res, string(p.l.b[curTStart:curTI])) default: - return nil, p.parseError("expected label or linebreak", t) + return nil, p.parseError("expected label or )", t) } t := p.nextToken() @@ -435,12 +450,15 @@ func (p *OpenMetricsParser) parseIdentifierLabels() ([]string, error) { case tComma: p.nextToken() continue - case tLinebreak: + case tParentClose: + if l := p.nextToken(); l != tLinebreak { + return nil, p.parseError("expected linebreak after )", l) + } // sort before returning labels sort.Strings(res) return res, nil default: - return nil, p.parseError("expected comma or linebreak", t) + return nil, p.parseError("expected comma or )", t) } } } diff --git a/model/textparse/openmetricsparse_test.go b/model/textparse/openmetricsparse_test.go index 9c9e4000a1..fbf37414e2 100644 --- a/model/textparse/openmetricsparse_test.go +++ b/model/textparse/openmetricsparse_test.go @@ -68,9 +68,8 @@ foo_total 17.0 1520879607.789 # {id="counter-test"} 5` input += "\n# HELP metric foo\x00bar" input += "\nnull_byte_metric{a=\"abc\x00\"} 1" - input += "\n# TYPE ii info" - input += "\n# ID aa,bb" - input += "\nii{aa=\"foo\",cc=\"bar\"} 1" + input += "\n# TYPE ii info(a,b)" + input += "\nii{a=\"foo\",b=\"bar\",c=\"far\"} 1" input += "\n# EOF\n" int64p := func(x int64) *int64 { return &x } @@ -79,215 +78,170 @@ foo_total 17.0 1520879607.789 # {id="counter-test"} 5` { m: "go_gc_duration_seconds", help: "A summary of the GC invocation durations.", - }, - { + }, { m: "go_gc_duration_seconds", typ: model.MetricTypeSummary, - }, - { + }, { m: "go_gc_duration_seconds", unit: "seconds", - }, - { + }, { m: `go_gc_duration_seconds{quantile="0"}`, v: 4.9351e-05, lset: labels.FromStrings("__name__", "go_gc_duration_seconds", "quantile", "0"), - }, - { + }, { m: `go_gc_duration_seconds{quantile="0.25"}`, v: 7.424100000000001e-05, lset: labels.FromStrings("__name__", "go_gc_duration_seconds", "quantile", "0.25"), - }, - { + }, { m: `go_gc_duration_seconds{quantile="0.5",a="b"}`, v: 8.3835e-05, lset: labels.FromStrings("__name__", "go_gc_duration_seconds", "quantile", "0.5", "a", "b"), - }, - { + }, { m: "nohelp1", help: "", - }, - { + }, { m: "help2", help: "escape \\ \n \\ \" \\x chars", - }, - { + }, { m: "nounit", unit: "", - }, - { + }, { m: `go_gc_duration_seconds{quantile="1.0",a="b"}`, v: 8.3835e-05, lset: labels.FromStrings("__name__", "go_gc_duration_seconds", "quantile", "1.0", "a", "b"), - }, - { + }, { m: `go_gc_duration_seconds_count`, v: 99, lset: labels.FromStrings("__name__", "go_gc_duration_seconds_count"), - }, - { + }, { m: `some:aggregate:rate5m{a_b="c"}`, v: 1, lset: labels.FromStrings("__name__", "some:aggregate:rate5m", "a_b", "c"), - }, - { + }, { m: "go_goroutines", help: "Number of goroutines that currently exist.", - }, - { + }, { m: "go_goroutines", typ: model.MetricTypeGauge, - }, - { + }, { m: `go_goroutines`, v: 33, t: int64p(123123), lset: labels.FromStrings("__name__", "go_goroutines"), - }, - { + }, { m: "hh", typ: model.MetricTypeHistogram, - }, - { + }, { m: `hh_bucket{le="+Inf"}`, v: 1, lset: labels.FromStrings("__name__", "hh_bucket", "le", "+Inf"), - }, - { + }, { m: "gh", typ: model.MetricTypeGaugeHistogram, - }, - { + }, { m: `gh_bucket{le="+Inf"}`, v: 1, lset: labels.FromStrings("__name__", "gh_bucket", "le", "+Inf"), - }, - { + }, { m: "hhh", typ: model.MetricTypeHistogram, - }, - { + }, { m: `hhh_bucket{le="+Inf"}`, v: 1, lset: labels.FromStrings("__name__", "hhh_bucket", "le", "+Inf"), e: &exemplar.Exemplar{Labels: labels.FromStrings("id", "histogram-bucket-test"), Value: 4}, - }, - { + }, { m: `hhh_count`, v: 1, lset: labels.FromStrings("__name__", "hhh_count"), e: &exemplar.Exemplar{Labels: labels.FromStrings("id", "histogram-count-test"), Value: 4}, - }, - { + }, { m: "ggh", typ: model.MetricTypeGaugeHistogram, - }, - { + }, { m: `ggh_bucket{le="+Inf"}`, v: 1, lset: labels.FromStrings("__name__", "ggh_bucket", "le", "+Inf"), e: &exemplar.Exemplar{Labels: labels.FromStrings("id", "gaugehistogram-bucket-test", "xx", "yy"), Value: 4, HasTs: true, Ts: 123123}, - }, - { + }, { m: `ggh_count`, v: 1, lset: labels.FromStrings("__name__", "ggh_count"), e: &exemplar.Exemplar{Labels: labels.FromStrings("id", "gaugehistogram-count-test", "xx", "yy"), Value: 4, HasTs: true, Ts: 123123}, - }, - { + }, { m: "smr_seconds", typ: model.MetricTypeSummary, - }, - { + }, { m: `smr_seconds_count`, v: 2, lset: labels.FromStrings("__name__", "smr_seconds_count"), e: &exemplar.Exemplar{Labels: labels.FromStrings("id", "summary-count-test"), Value: 1, HasTs: true, Ts: 123321}, - }, - { + }, { m: `smr_seconds_sum`, v: 42, lset: labels.FromStrings("__name__", "smr_seconds_sum"), e: &exemplar.Exemplar{Labels: labels.FromStrings("id", "summary-sum-test"), Value: 1, HasTs: true, Ts: 123321}, - }, - { + }, { m: "ii", typ: model.MetricTypeInfo, - }, - { + }, { m: `ii{foo="bar"}`, v: 1, lset: labels.FromStrings("__name__", "ii", "foo", "bar"), - }, - { + }, { m: "ss", typ: model.MetricTypeStateset, - }, - { + }, { m: `ss{ss="foo"}`, v: 1, lset: labels.FromStrings("__name__", "ss", "ss", "foo"), - }, - { + }, { m: `ss{ss="bar"}`, v: 0, lset: labels.FromStrings("__name__", "ss", "ss", "bar"), - }, - { + }, { m: `ss{A="a"}`, v: 0, lset: labels.FromStrings("A", "a", "__name__", "ss"), - }, - { + }, { m: "un", typ: model.MetricTypeUnknown, - }, - { + }, { m: "_metric_starting_with_underscore", v: 1, lset: labels.FromStrings("__name__", "_metric_starting_with_underscore"), - }, - { + }, { m: "testmetric{_label_starting_with_underscore=\"foo\"}", v: 1, lset: labels.FromStrings("__name__", "testmetric", "_label_starting_with_underscore", "foo"), - }, - { + }, { m: "testmetric{label=\"\\\"bar\\\"\"}", v: 1, lset: labels.FromStrings("__name__", "testmetric", "label", `"bar"`), - }, - { + }, { m: "foo", typ: model.MetricTypeCounter, - }, - { + }, { m: "foo_total", v: 17, lset: labels.FromStrings("__name__", "foo_total"), t: int64p(1520879607789), e: &exemplar.Exemplar{Labels: labels.FromStrings("id", "counter-test"), Value: 5}, - }, - { + }, { m: "metric", help: "foo\x00bar", - }, - { + }, { m: "null_byte_metric{a=\"abc\x00\"}", v: 1, lset: labels.FromStrings("__name__", "null_byte_metric", "a", "abc\x00"), - }, - { - m: "ii", - typ: model.MetricTypeInfo, - }, - { - m: "aa,bb", - }, - { - m: "ii{aa=\"foo\",cc=\"bar\"}", + }, { + m: "ii", + typ: model.MetricTypeInfo, + identlbs: []string{"a", "b"}, + }, { + m: "ii{a=\"foo\",b=\"bar\",c=\"far\"}", v: 1, - lset: labels.FromStrings("__name__", "ii", "aa", "foo", "cc", "bar"), + lset: labels.FromStrings("__name__", "ii", "a", "foo", "b", "bar", "c", "far"), }, } diff --git a/model/textparse/promlex.l b/model/textparse/promlex.l index e3294d3058..506b0cff51 100644 --- a/model/textparse/promlex.l +++ b/model/textparse/promlex.l @@ -30,7 +30,7 @@ const ( sExemplar sEValue sETimestamp - sMeta3 + sIdens ) // Lex is called by the parser generated by "go tool yacc" to obtain each diff --git a/model/textparse/promlex.l.go b/model/textparse/promlex.l.go index 926cff4dbc..6b411584f8 100644 --- a/model/textparse/promlex.l.go +++ b/model/textparse/promlex.l.go @@ -31,7 +31,7 @@ const ( sExemplar sEValue sETimestamp - sMeta3 + sIdens ) // Lex is called by the parser generated by "go tool yacc" to obtain each diff --git a/model/textparse/promparse.go b/model/textparse/promparse.go index 816dd9a11a..56b48e0d2b 100644 --- a/model/textparse/promparse.go +++ b/model/textparse/promparse.go @@ -58,6 +58,8 @@ const ( tBlank tMName tQString + tParentOpen + tParentClose tBraceOpen tBraceClose tLName @@ -85,8 +87,6 @@ func (t token) String() string { return "TYPE" case tUnit: return "UNIT" - case tIdens: - return "ID" case tEOFWord: return "EOFWORD" case tText: @@ -176,7 +176,7 @@ func NewPromParser(b []byte, st *labels.SymbolTable) Parser { } } -func (p *PromParser) Identifiers() []string { +func (p *PromParser) IdentifyingLabels() []string { return nil } diff --git a/model/textparse/promparse_test.go b/model/textparse/promparse_test.go index 3e92169b3d..b4b24168a8 100644 --- a/model/textparse/promparse_test.go +++ b/model/textparse/promparse_test.go @@ -18,7 +18,6 @@ import ( "errors" "io" "os" - "strings" "testing" "github.com/klauspost/compress/gzip" @@ -33,15 +32,16 @@ import ( ) type expectedParse struct { - lset labels.Labels - m string - t *int64 - v float64 - typ model.MetricType - help string - unit string - comment string - e *exemplar.Exemplar + lset labels.Labels + m string + t *int64 + v float64 + typ model.MetricType + help string + unit string + comment string + e *exemplar.Exemplar + identlbs []string } func TestPromParse(t *testing.T) { @@ -224,6 +224,7 @@ func checkParseResults(t *testing.T, p Parser, exp []expectedParse) { m, typ := p.Type() require.Equal(t, exp[i].m, string(m)) require.Equal(t, exp[i].typ, typ) + require.Equal(t, exp[i].identlbs, p.IdentifyingLabels()) case EntryHelp: m, h := p.Help() @@ -237,10 +238,8 @@ func checkParseResults(t *testing.T, p Parser, exp []expectedParse) { case EntryComment: require.Equal(t, exp[i].comment, string(p.Comment())) - case EntryIdent: - idens := p.Identifiers() - require.Equal(t, exp[i].m, strings.Join(idens, ",")) } + i++ } require.Len(t, exp, i) diff --git a/model/textparse/protobufparse.go b/model/textparse/protobufparse.go index b153136c92..c8a9eb2e01 100644 --- a/model/textparse/protobufparse.go +++ b/model/textparse/protobufparse.go @@ -91,7 +91,7 @@ func NewProtobufParser(b []byte, parseClassicHistograms bool, st *labels.SymbolT } } -func (p *ProtobufParser) Identifiers() []string { +func (p *ProtobufParser) IdentifyingLabels() []string { return nil }