diff --git a/model/textparse/README.md b/model/textparse/README.md index 697966f097..4fcfbceea4 100644 --- a/model/textparse/README.md +++ b/model/textparse/README.md @@ -3,4 +3,4 @@ In the rare case that you need to update the textparse lexers, edit promlex.l or `golex -o=promlex.l.go promlex.l` Note that you need golex installed: -`go get -u modernc.org/golex` \ No newline at end of file +`go install -u modernc.org/golex@latest` \ No newline at end of file diff --git a/model/textparse/interface.go b/model/textparse/interface.go index df01dbc34f..61acb8a066 100644 --- a/model/textparse/interface.go +++ b/model/textparse/interface.go @@ -51,6 +51,9 @@ 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 + // Comment returns the text of the current comment. // Must only be called after Next returned a comment entry. // The returned byte slice becomes invalid after the next call to Next. @@ -110,4 +113,5 @@ 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 9afbbbd8bd..9213754b6f 100644 --- a/model/textparse/openmetricslex.l +++ b/model/textparse/openmetricslex.l @@ -36,7 +36,7 @@ M [a-zA-Z_:] C [^\n] S [ ] -%x sComment sMeta1 sMeta2 sLabels sLValue sValue sTimestamp sExemplar sEValue sETimestamp +%x sComment sMeta1 sMeta2 sLabels sLValue sValue sTimestamp sExemplar sEValue sETimestamp sMeta3 %yyc c %yyn c = l.next() @@ -50,9 +50,13 @@ S [ ] TYPE{S} l.state = sMeta1; return tType UNIT{S} l.state = sMeta1; return tUnit "EOF"\n? l.state = sInit; return tEOFWord +IDENS{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 {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 c8789ef60d..e9cd4c02b1 100644 --- a/model/textparse/openmetricslex.l.go +++ b/model/textparse/openmetricslex.l.go @@ -39,23 +39,25 @@ yystate0: case 1: // start condition: sComment goto yystart6 case 2: // start condition: sMeta1 - goto yystart26 + goto yystart32 case 3: // start condition: sMeta2 - goto yystart31 + goto yystart37 case 4: // start condition: sLabels - goto yystart34 + goto yystart40 case 5: // start condition: sLValue - goto yystart42 + goto yystart48 case 6: // start condition: sValue - goto yystart46 + goto yystart52 case 7: // start condition: sTimestamp - goto yystart50 + goto yystart56 case 8: // start condition: sExemplar - goto yystart57 + goto yystart63 case 9: // start condition: sEValue - goto yystart62 - case 10: // start condition: sETimestamp goto yystart68 + case 10: // start condition: sETimestamp + goto yystart74 + case 11: // start condition: sMeta3 + goto yystart78 } yystate1: @@ -89,14 +91,14 @@ yystate4: c = l.next() switch { default: - goto yyrule9 + goto yyrule13 case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate4 } yystate5: c = l.next() - goto yyrule11 + goto yyrule15 yystate6: c = l.next() @@ -108,10 +110,12 @@ yystart6: goto yystate7 case c == 'H': goto yystate11 - case c == 'T': + case c == 'I': goto yystate16 + case c == 'T': + goto yystate22 case c == 'U': - goto yystate21 + goto yystate27 } yystate7: @@ -190,7 +194,7 @@ yystate16: switch { default: goto yyabort - case c == 'Y': + case c == 'D': goto yystate17 } @@ -199,7 +203,7 @@ yystate17: switch { default: goto yyabort - case c == 'P': + case c == 'E': goto yystate18 } @@ -208,7 +212,7 @@ yystate18: switch { default: goto yyabort - case c == 'E': + case c == 'N': goto yystate19 } @@ -217,29 +221,29 @@ yystate19: switch { default: goto yyabort - case c == ' ': + case c == 'S': goto yystate20 } yystate20: - c = l.next() - goto yyrule3 - -yystate21: c = l.next() switch { default: goto yyabort - case c == 'N': - goto yystate22 + case c == ' ': + goto yystate21 } +yystate21: + c = l.next() + goto yyrule6 + yystate22: c = l.next() switch { default: goto yyabort - case c == 'I': + case c == 'Y': goto yystate23 } @@ -248,7 +252,7 @@ yystate23: switch { default: goto yyabort - case c == 'T': + case c == 'P': goto yystate24 } @@ -257,406 +261,486 @@ yystate24: switch { default: goto yyabort - case c == ' ': + case c == 'E': goto yystate25 } yystate25: c = l.next() - goto yyrule4 + switch { + default: + goto yyabort + case c == ' ': + goto yystate26 + } yystate26: c = l.next() -yystart26: + goto yyrule3 + +yystate27: + c = l.next() + switch { + default: + goto yyabort + case c == 'N': + goto yystate28 + } + +yystate28: + c = l.next() + switch { + default: + goto yyabort + case c == 'I': + goto yystate29 + } + +yystate29: + c = l.next() + switch { + default: + goto yyabort + case c == 'T': + goto yystate30 + } + +yystate30: + c = l.next() + switch { + default: + goto yyabort + case c == ' ': + goto yystate31 + } + +yystate31: + c = l.next() + goto yyrule4 + +yystate32: + c = l.next() +yystart32: switch { default: goto yyabort case c == '"': - goto yystate27 + goto yystate33 case c == ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': - goto yystate30 + goto yystate36 } -yystate27: +yystate33: c = l.next() switch { default: goto yyabort case c == '"': - goto yystate28 + goto yystate34 case c == '\\': - goto yystate29 + goto yystate35 case c >= '\x01' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ': - goto yystate27 + goto yystate33 } -yystate28: +yystate34: c = l.next() - goto yyrule6 + goto yyrule7 -yystate29: +yystate35: c = l.next() switch { default: goto yyabort case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ': - goto yystate27 + goto yystate33 } -yystate30: +yystate36: c = l.next() switch { default: - goto yyrule7 + goto yyrule8 case c >= '0' && c <= ':' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': - goto yystate30 + goto yystate36 } -yystate31: +yystate37: c = l.next() -yystart31: +yystart37: switch { default: goto yyabort case c == ' ': - goto yystate32 + goto yystate38 } -yystate32: +yystate38: c = l.next() switch { default: goto yyabort case c == '\n': - goto yystate33 + goto yystate39 case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ': - goto yystate32 + goto yystate38 } -yystate33: +yystate39: c = l.next() - goto yyrule8 + goto yyrule9 -yystate34: +yystate40: c = l.next() -yystart34: +yystart40: switch { default: goto yyabort case c == '"': - goto yystate35 + goto yystate41 case c == ',': - goto yystate38 + goto yystate44 case c == '=': - goto yystate39 + goto yystate45 case c == '}': - goto yystate41 + goto yystate47 case c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': - goto yystate40 + goto yystate46 } -yystate35: +yystate41: c = l.next() switch { default: goto yyabort case c == '"': - goto yystate36 + goto yystate42 case c == '\\': - goto yystate37 + goto yystate43 case c >= '\x01' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ': - goto yystate35 + goto yystate41 } -yystate36: +yystate42: c = l.next() - goto yyrule13 + goto yyrule17 -yystate37: +yystate43: c = l.next() switch { default: goto yyabort case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ': - goto yystate35 + goto yystate41 } -yystate38: +yystate44: c = l.next() - goto yyrule16 + goto yyrule20 -yystate39: +yystate45: c = l.next() - goto yyrule15 + goto yyrule19 -yystate40: +yystate46: c = l.next() switch { default: - goto yyrule12 + goto yyrule16 case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': - goto yystate40 + goto yystate46 } -yystate41: +yystate47: c = l.next() - goto yyrule14 + goto yyrule18 -yystate42: +yystate48: c = l.next() -yystart42: +yystart48: switch { default: goto yyabort case c == '"': - goto yystate43 + goto yystate49 } -yystate43: +yystate49: c = l.next() switch { default: goto yyabort case c == '"': - goto yystate44 + goto yystate50 case c == '\\': - goto yystate45 + goto yystate51 case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ': - goto yystate43 + goto yystate49 } -yystate44: +yystate50: c = l.next() - goto yyrule17 + goto yyrule21 -yystate45: +yystate51: c = l.next() switch { default: goto yyabort case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ': - goto yystate43 + goto yystate49 } -yystate46: +yystate52: c = l.next() -yystart46: +yystart52: switch { default: goto yyabort case c == ' ': - goto yystate47 + goto yystate53 case c == '{': - goto yystate49 + goto yystate55 } -yystate47: +yystate53: c = l.next() switch { default: goto yyabort case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate48 + goto yystate54 } -yystate48: +yystate54: c = l.next() switch { default: - goto yyrule18 + goto yyrule22 case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate48 + goto yystate54 } -yystate49: +yystate55: c = l.next() - goto yyrule10 + goto yyrule14 -yystate50: +yystate56: c = l.next() -yystart50: +yystart56: switch { default: goto yyabort case c == ' ': - goto yystate52 + goto yystate58 case c == '\n': - goto yystate51 + goto yystate57 } -yystate51: +yystate57: c = l.next() - goto yyrule20 + goto yyrule24 -yystate52: +yystate58: c = l.next() switch { default: goto yyabort case c == '#': - goto yystate54 + goto yystate60 case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c == '!' || c == '"' || c >= '$' && c <= 'ÿ': - goto yystate53 + goto yystate59 } -yystate53: +yystate59: c = l.next() switch { default: - goto yyrule19 + goto yyrule23 case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate53 + goto yystate59 } -yystate54: +yystate60: c = l.next() switch { default: - goto yyrule19 + goto yyrule23 case c == ' ': - goto yystate55 + goto yystate61 case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate53 + goto yystate59 } -yystate55: +yystate61: c = l.next() switch { default: goto yyabort case c == '{': - goto yystate56 + goto yystate62 } -yystate56: +yystate62: c = l.next() - goto yyrule21 + goto yyrule25 -yystate57: +yystate63: c = l.next() -yystart57: +yystart63: switch { default: goto yyabort case c == ',': - goto yystate58 + goto yystate64 case c == '=': - goto yystate59 + goto yystate65 case c == '}': - goto yystate61 + goto yystate67 case c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': - goto yystate60 + goto yystate66 } -yystate58: +yystate64: c = l.next() - goto yyrule26 + goto yyrule30 -yystate59: +yystate65: c = l.next() - goto yyrule24 + goto yyrule28 -yystate60: +yystate66: c = l.next() switch { default: - goto yyrule22 + goto yyrule26 case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': - goto yystate60 + goto yystate66 } -yystate61: +yystate67: c = l.next() - goto yyrule23 + goto yyrule27 -yystate62: +yystate68: c = l.next() -yystart62: +yystart68: switch { default: goto yyabort case c == ' ': - goto yystate63 + goto yystate69 case c == '"': - goto yystate65 + goto yystate71 } -yystate63: +yystate69: c = l.next() switch { default: goto yyabort case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate64 + goto yystate70 } -yystate64: +yystate70: c = l.next() switch { default: - goto yyrule27 + goto yyrule31 case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate64 + goto yystate70 } -yystate65: +yystate71: c = l.next() switch { default: goto yyabort case c == '"': - goto yystate66 + goto yystate72 case c == '\\': - goto yystate67 + goto yystate73 case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '!' || c >= '#' && c <= '[' || c >= ']' && c <= 'ÿ': - goto yystate65 + goto yystate71 } -yystate66: +yystate72: c = l.next() - goto yyrule25 + goto yyrule29 -yystate67: +yystate73: c = l.next() switch { default: goto yyabort case c >= '\x01' && c <= '\t' || c >= '\v' && c <= 'ÿ': - goto yystate65 + goto yystate71 } -yystate68: +yystate74: c = l.next() -yystart68: +yystart74: switch { default: goto yyabort case c == ' ': - goto yystate70 + goto yystate76 case c == '\n': - goto yystate69 + goto yystate75 } -yystate69: +yystate75: c = l.next() - goto yyrule29 + goto yyrule33 -yystate70: +yystate76: c = l.next() switch { default: goto yyabort case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate71 + goto yystate77 } -yystate71: +yystate77: c = l.next() switch { default: - goto yyrule28 + goto yyrule32 case c >= '\x01' && c <= '\t' || c >= '\v' && c <= '\x1f' || c >= '!' && c <= 'ÿ': - goto yystate71 + goto yystate77 + } + +yystate78: + c = l.next() +yystart78: + switch { + default: + goto yyrule10 + case c == ',': + goto yystate80 + case c == '\n': + goto yystate79 + case c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate81 + } + +yystate79: + c = l.next() + goto yyrule12 + +yystate80: + c = l.next() + goto yyrule11 + +yystate81: + c = l.next() + switch { + default: + goto yyrule10 + case c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate81 } yyrule1: // #{S} @@ -688,133 +772,153 @@ yyrule5: // "EOF"\n? return tEOFWord goto yystate0 } -yyrule6: // \"(\\.|[^\\"])*\" +yyrule6: // IDENS{S} + { + l.state = sMeta3 + return tIdens + goto yystate0 + } +yyrule7: // \"(\\.|[^\\"])*\" { l.state = sMeta2 return tMName goto yystate0 } -yyrule7: // {M}({M}|{D})* +yyrule8: // {M}({M}|{D})* { l.state = sMeta2 return tMName goto yystate0 } -yyrule8: // {S}{C}*\n +yyrule9: // {S}{C}*\n { l.state = sInit return tText goto yystate0 } -yyrule9: // {M}({M}|{D})* +yyrule10: // {L}* + { + return tLName + } +yyrule11: // , + { + return tComma + } +yyrule12: // \n + { + l.state = sInit + return tLinebreak + goto yystate0 + } +yyrule13: // {M}({M}|{D})* { l.state = sValue return tMName goto yystate0 } -yyrule10: // \{ +yyrule14: // \{ { l.state = sLabels return tBraceOpen goto yystate0 } -yyrule11: // \{ +yyrule15: // \{ { l.state = sLabels return tBraceOpen goto yystate0 } -yyrule12: // {L}({L}|{D})* +yyrule16: // {L}({L}|{D})* { return tLName } -yyrule13: // \"(\\.|[^\\"])*\" +yyrule17: // \"(\\.|[^\\"])*\" { l.state = sLabels return tQString goto yystate0 } -yyrule14: // \} +yyrule18: // \} { l.state = sValue return tBraceClose goto yystate0 } -yyrule15: // = +yyrule19: // = { l.state = sLValue return tEqual goto yystate0 } -yyrule16: // , +yyrule20: // , { return tComma } -yyrule17: // \"(\\.|[^\\"\n])*\" +yyrule21: // \"(\\.|[^\\"\n])*\" { l.state = sLabels return tLValue goto yystate0 } -yyrule18: // {S}[^ \n]+ +yyrule22: // {S}[^ \n]+ { l.state = sTimestamp return tValue goto yystate0 } -yyrule19: // {S}[^ \n]+ +yyrule23: // {S}[^ \n]+ { return tTimestamp } -yyrule20: // \n +yyrule24: // \n { l.state = sInit return tLinebreak goto yystate0 } -yyrule21: // {S}#{S}\{ +yyrule25: // {S}#{S}\{ { l.state = sExemplar return tComment goto yystate0 } -yyrule22: // {L}({L}|{D})* +yyrule26: // {L}({L}|{D})* { return tLName } -yyrule23: // \} +yyrule27: // \} { l.state = sEValue return tBraceClose goto yystate0 } -yyrule24: // = +yyrule28: // = { l.state = sEValue return tEqual goto yystate0 } -yyrule25: // \"(\\.|[^\\"\n])*\" +yyrule29: // \"(\\.|[^\\"\n])*\" { l.state = sExemplar return tLValue goto yystate0 } -yyrule26: // , +yyrule30: // , { return tComma } -yyrule27: // {S}[^ \n]+ +yyrule31: // {S}[^ \n]+ { l.state = sETimestamp return tValue goto yystate0 } -yyrule28: // {S}[^ \n]+ +yyrule32: // {S}[^ \n]+ { return tTimestamp } -yyrule29: // \n +yyrule33: // \n if true { // avoid go vet determining the below panic will not be reached l.state = sInit return tLinebreak @@ -838,31 +942,34 @@ yyabort: // no lexem recognized goto yystate6 } if false { - goto yystate26 + goto yystate32 } if false { - goto yystate31 + goto yystate37 } if false { - goto yystate34 + goto yystate40 } if false { - goto yystate42 + goto yystate48 } if false { - goto yystate46 + goto yystate52 } if false { - goto yystate50 + goto yystate56 } if false { - goto yystate57 + goto yystate63 } if false { - goto yystate62 + goto yystate68 } if false { - goto yystate68 + goto yystate74 + } + if false { + goto yystate78 } } diff --git a/model/textparse/openmetricsparse.go b/model/textparse/openmetricsparse.go index b7ad1dd85c..db3c3039a3 100644 --- a/model/textparse/openmetricsparse.go +++ b/model/textparse/openmetricsparse.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:generate go get -u modernc.org/golex +//go:generate go install -u modernc.org/golex@latest //go:generate golex -o=openmetricslex.l.go openmetricslex.l package textparse @@ -21,6 +21,7 @@ import ( "fmt" "io" "math" + "sort" "strings" "unicode/utf8" @@ -89,6 +90,7 @@ type OpenMetricsParser struct { // of the label name and value start and end characters. offsets []int + identifierlbs []string eOffsets []int exemplar []byte exemplarVal float64 @@ -141,6 +143,10 @@ 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 +} + // Unit returns the metric name and unit in the current entry. // Must only be called after Next returned a unit entry. // The returned byte slices become invalid after the next call to Next. @@ -246,6 +252,7 @@ func (p *OpenMetricsParser) Next() (Entry, error) { p.start = p.l.i p.offsets = p.offsets[:0] + p.identifierlbs = p.identifierlbs[:0] p.eOffsets = p.eOffsets[:0] p.exemplar = p.exemplar[:0] p.exemplarVal = 0 @@ -259,6 +266,12 @@ 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: @@ -403,6 +416,35 @@ func (p *OpenMetricsParser) parseComment() error { return nil } +func (p *OpenMetricsParser) parseIdentifierLabels() ([]string, error) { + res := p.identifierlbs + t := p.nextToken() + for { + curTStart := p.l.start + curTI := p.l.i + + switch t { + case tLName: + res = append(res, string(p.l.b[curTStart:curTI])) + default: + return nil, p.parseError("expected label or linebreak", t) + } + + t := p.nextToken() + switch t { + case tComma: + p.nextToken() + continue + case tLinebreak: + // sort before returning labels + sort.Strings(res) + return res, nil + default: + return nil, p.parseError("expected comma or linebreak", t) + } + } +} + func (p *OpenMetricsParser) parseLVals(offsets []int, isExemplar bool) ([]int, error) { t := p.nextToken() for { diff --git a/model/textparse/openmetricsparse_test.go b/model/textparse/openmetricsparse_test.go index bc76a540d3..34657f91eb 100644 --- a/model/textparse/openmetricsparse_test.go +++ b/model/textparse/openmetricsparse_test.go @@ -68,6 +68,9 @@ 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# IDENS aa,bb" + input += "\nii{aa=\"foo\",cc=\"bar\"} 1" input += "\n# EOF\n" int64p := func(x int64) *int64 { return &x } @@ -76,163 +79,216 @@ 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\"}", + v: 1, + lset: labels.FromStrings("__name__", "ii", "aa", "foo", "cc", "bar"), + }, } p := NewOpenMetricsParser([]byte(input), labels.NewSymbolTable()) diff --git a/model/textparse/promlex.l b/model/textparse/promlex.l index e9fa1fb71c..e3294d3058 100644 --- a/model/textparse/promlex.l +++ b/model/textparse/promlex.l @@ -30,6 +30,7 @@ const ( sExemplar sEValue sETimestamp + sMeta3 ) // 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 a083e5549b..926cff4dbc 100644 --- a/model/textparse/promlex.l.go +++ b/model/textparse/promlex.l.go @@ -31,6 +31,7 @@ const ( sExemplar sEValue sETimestamp + sMeta3 ) // 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 a611f3aea7..9886cba654 100644 --- a/model/textparse/promparse.go +++ b/model/textparse/promparse.go @@ -66,6 +66,7 @@ const ( tEqual tTimestamp tValue + tIdens ) func (t token) String() string { @@ -84,6 +85,8 @@ func (t token) String() string { return "TYPE" case tUnit: return "UNIT" + case tIdens: + return "IDENS" case tEOFWord: return "EOFWORD" case tText: @@ -173,6 +176,10 @@ func NewPromParser(b []byte, st *labels.SymbolTable) Parser { } } +func (p *PromParser) Identifiers() []string { + return nil +} + // Series returns the bytes of the series, the timestamp if set, and the value // of the current sample. func (p *PromParser) Series() ([]byte, *int64, float64) { diff --git a/model/textparse/promparse_test.go b/model/textparse/promparse_test.go index 66986291d7..3e92169b3d 100644 --- a/model/textparse/promparse_test.go +++ b/model/textparse/promparse_test.go @@ -18,6 +18,7 @@ import ( "errors" "io" "os" + "strings" "testing" "github.com/klauspost/compress/gzip" @@ -236,8 +237,10 @@ 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 ea3a2e1a34..b153136c92 100644 --- a/model/textparse/protobufparse.go +++ b/model/textparse/protobufparse.go @@ -91,6 +91,10 @@ func NewProtobufParser(b []byte, parseClassicHistograms bool, st *labels.SymbolT } } +func (p *ProtobufParser) Identifiers() []string { + return nil +} + // Series returns the bytes of a series with a simple float64 as a // value, the timestamp if set, and the value of the current sample. func (p *ProtobufParser) Series() ([]byte, *int64, float64) {