UTF-8: Add support for parsing UTF8 metric and label names

This adds support for the new grammar of `{"metric_name", "l1"="val",}`. This grammar will also be valid for non-UTF-8 names.
UTF-8 names will not be considered valid unless model.NameValidationScheme is changed.

Signed-off-by: Owen Williams <owen.williams@grafana.com>
This commit is contained in:
Owen Williams 2024-02-08 14:38:07 -05:00
parent 4fddb5a382
commit 58eb877f96
7 changed files with 67 additions and 87 deletions

1
go.mod
View file

@ -203,6 +203,7 @@ require (
)
replace (
github.com/prometheus/common => /home/owilliams/src/third_party/common
k8s.io/klog => github.com/simonpasquier/klog-gokit v0.3.0
k8s.io/klog/v2 => github.com/simonpasquier/klog-gokit/v3 v3.3.0
)

18
go.sum
View file

@ -639,8 +639,6 @@ github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs=
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
@ -802,16 +800,13 @@ github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmn
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4=
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
@ -1141,7 +1136,6 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
@ -1163,7 +1157,6 @@ github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZ
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
@ -1208,7 +1201,6 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
@ -1246,7 +1238,6 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
@ -1328,11 +1319,6 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ=
github.com/prometheus/common v0.46.1-0.20240206181200-773d5664eb8d h1:cX7RXwXdSy339hITL5WeF6EeHIvAK6xUIT23xWYBSkg=
github.com/prometheus/common v0.46.1-0.20240206181200-773d5664eb8d/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/common/assets v0.2.0 h1:0P5OrzoHrYBOSM1OigWL3mY8ZvV2N4zIE/5AahrSrfM=
github.com/prometheus/common/assets v0.2.0/go.mod h1:D17UVUE12bHbim7HzwUvtqm6gwBEaDQ0F+hIGbFbccI=
github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4=
@ -1594,7 +1580,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -1625,7 +1610,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -2228,7 +2212,6 @@ google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -2249,7 +2232,6 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

View file

@ -26,6 +26,7 @@ import (
"github.com/prometheus/common/model"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/model/exemplar"
"github.com/prometheus/prometheus/model/histogram"
"github.com/prometheus/prometheus/model/labels"
@ -225,11 +226,12 @@ func (p *OpenMetricsParser) nextToken() token {
}
func (p *OpenMetricsParser) parseError(exp string, got token) error {
e := p.l.i + 1
e := p.l.i + 80
if len(p.l.b) < e {
e = len(p.l.b)
}
return fmt.Errorf("%s, got %q (%q) while parsing: %q", exp, p.l.b[p.l.start:e], got, p.l.b[p.start:e])
start := int(math.Max(0, float64(p.start-80)))
return fmt.Errorf("%s, got %q (%q) while parsing: %q", exp, p.l.b[p.l.start:e], got, p.l.b[start:e])
}
// Next advances the parser to the next sample. It returns false if no
@ -253,6 +255,7 @@ func (p *OpenMetricsParser) Next() (Entry, error) {
case tEOF:
return EntryInvalid, errors.New("data does not end with # EOF")
case tHelp, tType, tUnit:
tStart := p.l.start
switch t2 := p.nextToken(); t2 {
case tMName:
mStart := p.l.start
@ -263,7 +266,7 @@ func (p *OpenMetricsParser) Next() (Entry, error) {
}
p.offsets = append(p.offsets, mStart, mEnd)
default:
return EntryInvalid, p.parseError("expected metric name after "+t.String(), t2)
return EntryInvalid, p.parseError("expected metric name after "+t.String()+" "+string(p.l.b), t2)
}
switch t2 := p.nextToken(); t2 {
case tText:
@ -273,7 +276,11 @@ func (p *OpenMetricsParser) Next() (Entry, error) {
p.text = []byte{}
}
default:
return EntryInvalid, fmt.Errorf("expected text in %s", t.String())
end := tStart + 40
if end >= len(p.l.b) {
end = len(p.l.b) - 1
}
return EntryInvalid, fmt.Errorf("expected text in %s: got %v (%v)", t.String(), t2.String(), string(p.l.b[tStart:end]))
}
switch t {
case tType:
@ -461,10 +468,6 @@ func (p *OpenMetricsParser) parseLVals(offsets []int) ([]int, error) {
// parseMetricSuffix parses the end of the line after the metric name and
// labels. It starts parsing with the provided token.
func (p *OpenMetricsParser) parseMetricSuffix(t token) (Entry, error) {
if p.offsets[0] == -1 {
return EntryInvalid, fmt.Errorf("metric name not set while parsing: %q", p.l.b[p.start:p.l.i])
}
var err error
p.val, err = p.getFloatValue(t, "metric")
if err != nil {
@ -486,7 +489,7 @@ func (p *OpenMetricsParser) parseMetricSuffix(t token) (Entry, error) {
var ts float64
// A float is enough to hold what we need for millisecond resolution.
if ts, err = parseFloat(yoloString(p.l.buf()[1:])); err != nil {
return EntryInvalid, fmt.Errorf("%w while parsing: %q", err, p.l.b[p.start:p.l.i])
return EntryInvalid, fmt.Errorf("%v while parsing: %q", err, p.l.b[p.start:p.l.i])
}
if math.IsNaN(ts) || math.IsInf(ts, 0) {
return EntryInvalid, fmt.Errorf("invalid timestamp %f", ts)

View file

@ -21,6 +21,7 @@ import (
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/model/exemplar"
"github.com/prometheus/prometheus/model/labels"
)
@ -301,10 +302,9 @@ foo_total 17.0 1520879607.789 # {id="counter-test"} 5`
}
func TestUTF8OpenMetricsParse(t *testing.T) {
oldValidationScheme := model.NameValidationScheme
model.NameValidationScheme = model.UTF8Validation
defer func() {
model.NameValidationScheme = oldValidationScheme
defer func(){
model.NameValidationScheme = model.LegacyValidation
}()
input := `# HELP "go.gc_duration_seconds" A summary of the GC invocation durations.
@ -325,7 +325,7 @@ func TestUTF8OpenMetricsParse(t *testing.T) {
m string
t *int64
v float64
typ model.MetricType
typ MetricType
help string
unit string
comment string
@ -336,7 +336,7 @@ func TestUTF8OpenMetricsParse(t *testing.T) {
help: "A summary of the GC invocation durations.",
}, {
m: "go.gc_duration_seconds",
typ: model.MetricTypeSummary,
typ: MetricTypeSummary,
}, {
m: "go.gc_duration_seconds",
unit: "seconds",
@ -467,19 +467,19 @@ func TestOpenMetricsParseErrors(t *testing.T) {
},
{
input: "a\n#EOF\n",
err: "expected value after metric, got \"\\n\" (\"INVALID\") while parsing: \"a\\n\"",
err: "expected value after metric, got \"\\n#EOF\\n\" (\"INVALID\") while parsing: \"a\\n#EOF\\n\"",
},
{
input: "\n\n#EOF\n",
err: "expected a valid start token, got \"\\n\" (\"INVALID\") while parsing: \"\\n\"",
err: "expected a valid start token, got \"\\n\\n#EOF\\n\" (\"INVALID\") while parsing: \"\\n\\n#EOF\\n\"",
},
{
input: " a 1\n#EOF\n",
err: "expected a valid start token, got \" \" (\"INVALID\") while parsing: \" \"",
err: "expected a valid start token, got \" a 1\\n#EOF\\n\" (\"INVALID\") while parsing: \" a 1\\n#EOF\\n\"",
},
{
input: "9\n#EOF\n",
err: "expected a valid start token, got \"9\" (\"INVALID\") while parsing: \"9\"",
err: "expected a valid start token, got \"9\\n#EOF\\n\" (\"INVALID\") while parsing: \"9\\n#EOF\\n\"",
},
{
input: "# TYPE u untyped\n#EOF\n",
@ -491,15 +491,15 @@ func TestOpenMetricsParseErrors(t *testing.T) {
},
{
input: "# TYPE c counter\n#EOF\n",
err: "expected a valid start token, got \"# \" (\"INVALID\") while parsing: \"# \"",
err: "expected a valid start token, got \"# TYPE c counter\\n#EOF\\n\" (\"INVALID\") while parsing: \"# TYPE c counter\\n#EOF\\n\"",
},
{
input: "# TYPE \n#EOF\n",
err: "expected metric name after TYPE, got \"\\n\" (\"INVALID\") while parsing: \"# TYPE \\n\"",
err: "expected metric name after TYPE # TYPE \n#EOF\n, got \"\\n#EOF\\n\" (\"INVALID\") while parsing: \"# TYPE \\n#EOF\\n\"",
},
{
input: "# TYPE m\n#EOF\n",
err: "expected text in TYPE",
err: "expected text in TYPE: got INVALID (# TYPE m\n#EOF)",
},
{
input: "# UNIT metric suffix\n#EOF\n",
@ -515,23 +515,23 @@ func TestOpenMetricsParseErrors(t *testing.T) {
},
{
input: "# UNIT \n#EOF\n",
err: "expected metric name after UNIT, got \"\\n\" (\"INVALID\") while parsing: \"# UNIT \\n\"",
err: "expected metric name after UNIT # UNIT \n#EOF\n, got \"\\n#EOF\\n\" (\"INVALID\") while parsing: \"# UNIT \\n#EOF\\n\"",
},
{
input: "# UNIT m\n#EOF\n",
err: "expected text in UNIT",
err: "expected text in UNIT: got INVALID (# UNIT m\n#EOF)",
},
{
input: "# HELP \n#EOF\n",
err: "expected metric name after HELP, got \"\\n\" (\"INVALID\") while parsing: \"# HELP \\n\"",
err: "expected metric name after HELP # HELP \n#EOF\n, got \"\\n#EOF\\n\" (\"INVALID\") while parsing: \"# HELP \\n#EOF\\n\"",
},
{
input: "# HELP m\n#EOF\n",
err: "expected text in HELP",
err: "expected text in HELP: got INVALID (# HELP m\n#EOF)",
},
{
input: "a\t1\n#EOF\n",
err: "expected value after metric, got \"\\t\" (\"INVALID\") while parsing: \"a\\t\"",
err: "expected value after metric, got \"\\t1\\n#EOF\\n\" (\"INVALID\") while parsing: \"a\\t1\\n#EOF\\n\"",
},
{
input: "a 1\t2\n#EOF\n",
@ -539,11 +539,11 @@ func TestOpenMetricsParseErrors(t *testing.T) {
},
{
input: "a 1 2 \n#EOF\n",
err: "expected next entry after timestamp, got \" \\n\" (\"INVALID\") while parsing: \"a 1 2 \\n\"",
err: "expected next entry after timestamp, got \" \\n#EOF\\n\" (\"INVALID\") while parsing: \"a 1 2 \\n#EOF\\n\"",
},
{
input: "a 1 2 #\n#EOF\n",
err: "expected next entry after timestamp, got \" #\\n\" (\"TIMESTAMP\") while parsing: \"a 1 2 #\\n\"",
err: "expected next entry after timestamp, got \" #\\n#EOF\\n\" (\"TIMESTAMP\") while parsing: \"a 1 2 #\\n#EOF\\n\"",
},
{
input: "a 1 1z\n#EOF\n",
@ -551,7 +551,7 @@ func TestOpenMetricsParseErrors(t *testing.T) {
},
{
input: " # EOF\n",
err: "expected a valid start token, got \" \" (\"INVALID\") while parsing: \" \"",
err: "expected a valid start token, got \" # EOF\\n\" (\"INVALID\") while parsing: \" # EOF\\n\"",
},
{
input: "# EOF\na 1",
@ -567,7 +567,7 @@ func TestOpenMetricsParseErrors(t *testing.T) {
},
{
input: "#\tTYPE c counter\n",
err: "expected a valid start token, got \"#\\t\" (\"INVALID\") while parsing: \"#\\t\"",
err: "expected a valid start token, got \"#\\tTYPE c counter\\n\" (\"INVALID\") while parsing: \"#\\tTYPE c counter\\n\"",
},
{
input: "# TYPE c counter\n",
@ -575,31 +575,31 @@ func TestOpenMetricsParseErrors(t *testing.T) {
},
{
input: "a 1 1 1\n# EOF\n",
err: "expected next entry after timestamp, got \" 1\\n\" (\"TIMESTAMP\") while parsing: \"a 1 1 1\\n\"",
err: "expected next entry after timestamp, got \" 1\\n# EOF\\n\" (\"TIMESTAMP\") while parsing: \"a 1 1 1\\n# EOF\\n\"",
},
{
input: "a{b='c'} 1\n# EOF\n",
err: "expected label value, got \"'\" (\"INVALID\") while parsing: \"a{b='\"",
err: "expected label value, got \"'c'} 1\\n# EOF\\n\" (\"INVALID\") while parsing: \"a{b='c'} 1\\n# EOF\\n\"",
},
{
input: "a{,b=\"c\"} 1\n# EOF\n",
err: "expected label name, got \",b\" (\"COMMA\") while parsing: \"a{,b\"",
err: "expected label name, got \",b=\\\"c\\\"} 1\\n# EOF\\n\" (\"COMMA\") while parsing: \"a{,b=\\\"c\\\"} 1\\n# EOF\\n\"",
},
{
input: "a{b=\"c\"d=\"e\"} 1\n# EOF\n",
err: "expected comma or brace close, got \"d=\" (\"LNAME\") while parsing: \"a{b=\\\"c\\\"d=\"",
err: "expected comma or brace close, got \"d=\\\"e\\\"} 1\\n# EOF\\n\" (\"LNAME\") while parsing: \"a{b=\\\"c\\\"d=\\\"e\\\"} 1\\n# EOF\\n\"",
},
{
input: "a{b=\"c\",,d=\"e\"} 1\n# EOF\n",
err: "expected label name, got \",d\" (\"COMMA\") while parsing: \"a{b=\\\"c\\\",,d\"",
err: "expected label name, got \",d=\\\"e\\\"} 1\\n# EOF\\n\" (\"COMMA\") while parsing: \"a{b=\\\"c\\\",,d=\\\"e\\\"} 1\\n# EOF\\n\"",
},
{
input: "a{b=\n# EOF\n",
err: "expected label value, got \"\\n\" (\"INVALID\") while parsing: \"a{b=\\n\"",
err: "expected label value, got \"\\n# EOF\\n\" (\"INVALID\") while parsing: \"a{b=\\n# EOF\\n\"",
},
{
input: "a{\xff=\"foo\"} 1\n# EOF\n",
err: "expected label name, got \"\\xff\" (\"INVALID\") while parsing: \"a{\\xff\"",
err: "expected label name, got \"\\xff=\\\"foo\\\"} 1\\n# EOF\\n\" (\"INVALID\") while parsing: \"a{\\xff=\\\"foo\\\"} 1\\n# EOF\\n\"",
},
{
input: "a{b=\"\xff\"} 1\n# EOF\n",
@ -611,11 +611,11 @@ func TestOpenMetricsParseErrors(t *testing.T) {
},
{
input: "something_weird{problem=\"\n# EOF\n",
err: "expected label value, got \"\\\"\\n\" (\"INVALID\") while parsing: \"something_weird{problem=\\\"\\n\"",
err: "expected label value, got \"\\\"\\n# EOF\\n\" (\"INVALID\") while parsing: \"something_weird{problem=\\\"\\n# EOF\\n\"",
},
{
input: "empty_label_name{=\"\"} 0\n# EOF\n",
err: "expected label name, got \"=\\\"\" (\"EQUAL\") while parsing: \"empty_label_name{=\\\"\"",
err: "expected label name, got \"=\\\"\\\"} 0\\n# EOF\\n\" (\"EQUAL\") while parsing: \"empty_label_name{=\\\"\\\"} 0\\n# EOF\\n\"",
},
{
input: "foo 1_2\n\n# EOF\n",
@ -635,11 +635,11 @@ func TestOpenMetricsParseErrors(t *testing.T) {
},
{
input: "custom_metric_total 1 # {aa=bb}\n# EOF\n",
err: "expected label value, got \"b\" (\"INVALID\") while parsing: \"custom_metric_total 1 # {aa=b\"",
err: "expected label value, got \"bb}\\n# EOF\\n\" (\"INVALID\") while parsing: \"custom_metric_total 1 # {aa=bb}\\n# EOF\\n\"",
},
{
input: "custom_metric_total 1 # {aa=\"bb\"}\n# EOF\n",
err: "expected value after exemplar labels, got \"\\n\" (\"INVALID\") while parsing: \"custom_metric_total 1 # {aa=\\\"bb\\\"}\\n\"",
err: "expected value after exemplar labels, got \"\\n# EOF\\n\" (\"INVALID\") while parsing: \"custom_metric_total 1 # {aa=\\\"bb\\\"}\\n# EOF\\n\"",
},
{
input: `custom_metric_total 1 # {aa="bb"}`,
@ -647,7 +647,7 @@ func TestOpenMetricsParseErrors(t *testing.T) {
},
{
input: `custom_metric_total 1 # {aa="bb",,cc="dd"} 1`,
err: "expected label name, got \",c\" (\"COMMA\") while parsing: \"custom_metric_total 1 # {aa=\\\"bb\\\",,c\"",
err: "expected label name, got \",cc=\\\"dd\\\"} 1\" (\"COMMA\") while parsing: \"custom_metric_total 1 # {aa=\\\"bb\\\",,cc=\\\"dd\\\"} 1\"",
},
{
input: `custom_metric_total 1 # {aa="bb"} 1_2`,
@ -667,11 +667,11 @@ func TestOpenMetricsParseErrors(t *testing.T) {
},
{
input: `custom_metric_total 1 # {aa=\"\xff\"} 9.0`,
err: "expected label value, got \"\\\\\" (\"INVALID\") while parsing: \"custom_metric_total 1 # {aa=\\\\\"",
err: "expected label value, got \"\\\\\\\"\\\\xff\\\\\\\"} 9.0\" (\"INVALID\") while parsing: \"custom_metric_total 1 # {aa=\\\\\\\"\\\\xff\\\\\\\"} 9.0\"",
},
{
input: `{b="c",} 1`,
err: "metric name not set while parsing: \"{b=\\\"c\\\",} 1\"",
err: "data does not end with # EOF",
},
{
input: `a 1 NaN`,
@ -732,7 +732,7 @@ func TestOMNullByteHandling(t *testing.T) {
},
{
input: "a{b=\x00\"ssss\"} 1\n# EOF\n",
err: "expected label value, got \"\\x00\" (\"INVALID\") while parsing: \"a{b=\\x00\"",
err: "expected label value, got \"\\x00\\\"ssss\\\"} 1\\n# EOF\\n\" (\"INVALID\") while parsing: \"a{b=\\x00\\\"ssss\\\"} 1\\n# EOF\\n\"",
},
{
input: "a{b=\"\x00",
@ -740,11 +740,11 @@ func TestOMNullByteHandling(t *testing.T) {
},
{
input: "a{b\x00=\"hiih\"} 1",
err: "expected equal, got \"\\x00\" (\"INVALID\") while parsing: \"a{b\\x00\"",
err: "expected equal, got \"\\x00=\\\"hiih\\\"}\\t1\" (\"INVALID\") while parsing: \"a{b\\x00=\\\"hiih\\\"}\\t1\"",
},
{
input: "a\x00{b=\"ddd\"} 1",
err: "expected value after metric, got \"\\x00\" (\"INVALID\") while parsing: \"a\\x00\"",
err: "expected value after metric, got \"\\x00{b=\\\"ddd\\\"} 1\" (\"INVALID\") while parsing: \"a\\x00{b=\\\"ddd\\\"} 1\"",
},
{
input: "#",
@ -756,11 +756,11 @@ func TestOMNullByteHandling(t *testing.T) {
},
{
input: "custom_metric_total 1 # {b=\x00\"ssss\"} 1\n",
err: "expected label value, got \"\\x00\" (\"INVALID\") while parsing: \"custom_metric_total 1 # {b=\\x00\"",
err: "expected label value, got \"\\x00\\\"ssss\\\"} 1\\n\" (\"INVALID\") while parsing: \"custom_metric_total 1 # {b=\\x00\\\"ssss\\\"} 1\\n\"",
},
{
input: "custom_metric_total 1 # {b=\"\x00ss\"} 1\n",
err: "expected label value, got \"\\\"\\x00\" (\"INVALID\") while parsing: \"custom_metric_total 1 # {b=\\\"\\x00\"",
err: "expected label value, got \"\\\"\\x00ss\\\"} 1\\n\" (\"INVALID\") while parsing: \"custom_metric_total 1 # {b=\\\"\\x00ss\\\"} 1\\n\"",
},
}

View file

@ -28,6 +28,7 @@ import (
"github.com/prometheus/common/model"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/model/exemplar"
"github.com/prometheus/prometheus/model/histogram"
"github.com/prometheus/prometheus/model/labels"
@ -445,8 +446,11 @@ func (p *PromParser) parseLVals() error {
// Free trailing commas are allowed. NOTE: this allows spaces between label
// names, unlike in OpenMetrics. It is not clear if this is intended or an
// accidental bug.
if t = p.nextToken(); t == tComma {
t = p.nextToken()
if t == tComma {
t = p.nextToken()
} else if t != tBraceClose {
return p.parseError("expected comma or brace close", t)
}
}
}
@ -454,15 +458,12 @@ func (p *PromParser) parseLVals() error {
// parseMetricSuffix parses the end of the line after the metric name and
// labels. It starts parsing with the provided token.
func (p *PromParser) parseMetricSuffix(t token) (Entry, error) {
if p.offsets[0] == -1 {
return EntryInvalid, fmt.Errorf("metric name not set while parsing: %q", p.l.b[p.start:p.l.i])
}
if t != tValue {
return EntryInvalid, p.parseError("expected value after metric", t)
}
var err error
if p.val, err = parseFloat(yoloString(p.l.buf())); err != nil {
return EntryInvalid, fmt.Errorf("%w while parsing: %q", err, p.l.b[p.start:p.l.i])
return EntryInvalid, fmt.Errorf("%v while parsing: %q", err, p.l.b[p.start:p.l.i])
}
// Ensure canonical NaN value.
if math.IsNaN(p.val) {
@ -475,7 +476,7 @@ func (p *PromParser) parseMetricSuffix(t token) (Entry, error) {
case tTimestamp:
p.hasTS = true
if p.ts, err = strconv.ParseInt(yoloString(p.l.buf()), 10, 64); err != nil {
return EntryInvalid, fmt.Errorf("%w while parsing: %q", err, p.l.b[p.start:p.l.i])
return EntryInvalid, fmt.Errorf("%v while parsing: %q", err, p.l.b[p.start:p.l.i])
}
if t2 := p.nextToken(); t2 != tLinebreak {
return EntryInvalid, p.parseError("expected next entry after timestamp", t2)
@ -484,6 +485,9 @@ func (p *PromParser) parseMetricSuffix(t token) (Entry, error) {
return EntryInvalid, p.parseError("expected timestamp or new record", t)
}
if p.offsets[0] == -1 {
return EntryInvalid, fmt.Errorf("metric name not set while parsing: %q", p.l.b[p.start:p.l.i])
}
return EntrySeries, nil
}

View file

@ -219,10 +219,9 @@ testmetric{label="\"bar\""} 1`
}
func TestUTF8PromParse(t *testing.T) {
oldValidationScheme := model.NameValidationScheme
model.NameValidationScheme = model.UTF8Validation
defer func() {
model.NameValidationScheme = oldValidationScheme
model.NameValidationScheme = model.LegacyValidation
}()
input := `# HELP "go.gc_duration_seconds" A summary of the GC invocation durations.
@ -243,7 +242,7 @@ func TestUTF8PromParse(t *testing.T) {
m string
t *int64
v float64
typ model.MetricType
typ MetricType
help string
comment string
}{
@ -252,7 +251,7 @@ func TestUTF8PromParse(t *testing.T) {
help: "A summary of the GC invocation durations.",
}, {
m: "go.gc_duration_seconds",
typ: model.MetricTypeSummary,
typ: MetricTypeSummary,
}, {
m: `{"go.gc_duration_seconds",quantile="0"}`,
v: 4.9351e-05,
@ -393,7 +392,7 @@ func TestPromParseErrors(t *testing.T) {
},
{
input: `{a="ok"} 1`,
err: "metric name not set while parsing: \"{a=\\\"ok\\\"} 1\"",
err: "metric name not set while parsing: \"{a=\\\"ok\\\"} 1\\n\"",
},
{
input: "# TYPE #\n#EOF\n",

View file

@ -1404,10 +1404,7 @@ yydefault:
case 85:
yyDollar = yyS[yypt-3 : yypt+1]
{
// label_matchers
// name := yylex.(*parser).findNameMatcher($2)
yyVAL.node = &VectorSelector{
// Name: name,
LabelMatchers: yyDollar[2].matchers,
PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[3].item),
}
@ -1415,9 +1412,7 @@ yydefault:
case 86:
yyDollar = yyS[yypt-4 : yypt+1]
{
// name := yylex.(*parser).findNameMatcher($2)
yyVAL.node = &VectorSelector{
// Name: name,
LabelMatchers: yyDollar[2].matchers,
PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[4].item),
}
@ -1463,7 +1458,6 @@ yydefault:
case 93:
yyDollar = yyS[yypt-1 : yypt+1]
{
// metric name matcher!
yyVAL.matcher = yylex.(*parser).newMetricNameMatcher(yyDollar[1].item)
}
case 94:
@ -1499,7 +1493,6 @@ yydefault:
case 121:
yyDollar = yyS[yypt-3 : yypt+1]
{
// LEFT_BRACE label_set_list RIGHT_BRACE
yyVAL.labels = labels.New(yyDollar[2].lblList...)
}
case 122:
@ -1536,7 +1529,6 @@ yydefault:
case 128:
yyDollar = yyS[yypt-3 : yypt+1]
{
// ident eq string
yyVAL.label = labels.Label{Name: yyDollar[1].item.Val, Value: yylex.(*parser).unquoteString(yyDollar[3].item.Val)}
}
case 129:
@ -1837,7 +1829,6 @@ yydefault:
case 226:
yyDollar = yyS[yypt-1 : yypt+1]
{
// string_identifier
yyVAL.item = Item{
Typ: METRIC_IDENTIFIER,
Pos: yyDollar[1].item.PositionRange().Start,