Update package model/textparse for new labels.Labels type

Parse metrics using labels.ScratchBuilder, so we reduce assumptions about
internals of Labels.

Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
This commit is contained in:
Bryan Boreham 2022-03-09 22:13:50 +00:00
parent fe9fe0e1e5
commit 1f04899ae3
3 changed files with 34 additions and 42 deletions

View file

@ -22,7 +22,6 @@ import (
"fmt" "fmt"
"io" "io"
"math" "math"
"sort"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
@ -82,6 +81,7 @@ func (l *openMetricsLexer) Error(es string) {
// This is based on the working draft https://docs.google.com/document/u/1/d/1KwV0mAXwwbvvifBvDKH_LU1YjyXE_wxCkHNoCGq1GX0/edit // This is based on the working draft https://docs.google.com/document/u/1/d/1KwV0mAXwwbvvifBvDKH_LU1YjyXE_wxCkHNoCGq1GX0/edit
type OpenMetricsParser struct { type OpenMetricsParser struct {
l *openMetricsLexer l *openMetricsLexer
builder labels.ScratchBuilder
series []byte series []byte
text []byte text []byte
mtype MetricType mtype MetricType
@ -158,14 +158,11 @@ func (p *OpenMetricsParser) Comment() []byte {
// Metric writes the labels of the current sample into the passed labels. // Metric writes the labels of the current sample into the passed labels.
// It returns the string from which the metric was parsed. // It returns the string from which the metric was parsed.
func (p *OpenMetricsParser) Metric(l *labels.Labels) string { func (p *OpenMetricsParser) Metric(l *labels.Labels) string {
// Allocate the full immutable string immediately, so we just // Copy the buffer to a string: this is only necessary for the return value.
// have to create references on it below.
s := string(p.series) s := string(p.series)
*l = append(*l, labels.Label{ p.builder.Reset()
Name: labels.MetricName, p.builder.Add(labels.MetricName, s[:p.offsets[0]-p.start])
Value: s[:p.offsets[0]-p.start],
})
for i := 1; i < len(p.offsets); i += 4 { for i := 1; i < len(p.offsets); i += 4 {
a := p.offsets[i] - p.start a := p.offsets[i] - p.start
@ -173,16 +170,16 @@ func (p *OpenMetricsParser) Metric(l *labels.Labels) string {
c := p.offsets[i+2] - p.start c := p.offsets[i+2] - p.start
d := p.offsets[i+3] - p.start d := p.offsets[i+3] - p.start
value := s[c:d]
// Replacer causes allocations. Replace only when necessary. // Replacer causes allocations. Replace only when necessary.
if strings.IndexByte(s[c:d], byte('\\')) >= 0 { if strings.IndexByte(s[c:d], byte('\\')) >= 0 {
*l = append(*l, labels.Label{Name: s[a:b], Value: lvalReplacer.Replace(s[c:d])}) value = lvalReplacer.Replace(value)
continue
} }
*l = append(*l, labels.Label{Name: s[a:b], Value: s[c:d]}) p.builder.Add(s[a:b], value)
} }
// Sort labels. p.builder.Sort()
sort.Sort(*l) *l = p.builder.Labels()
return s return s
} }
@ -204,17 +201,18 @@ func (p *OpenMetricsParser) Exemplar(e *exemplar.Exemplar) bool {
e.Ts = p.exemplarTs e.Ts = p.exemplarTs
} }
p.builder.Reset()
for i := 0; i < len(p.eOffsets); i += 4 { for i := 0; i < len(p.eOffsets); i += 4 {
a := p.eOffsets[i] - p.start a := p.eOffsets[i] - p.start
b := p.eOffsets[i+1] - p.start b := p.eOffsets[i+1] - p.start
c := p.eOffsets[i+2] - p.start c := p.eOffsets[i+2] - p.start
d := p.eOffsets[i+3] - p.start d := p.eOffsets[i+3] - p.start
e.Labels = append(e.Labels, labels.Label{Name: s[a:b], Value: s[c:d]}) p.builder.Add(s[a:b], s[c:d])
} }
// Sort the labels. p.builder.Sort()
sort.Sort(e.Labels) e.Labels = p.builder.Labels()
return true return true
} }

View file

@ -21,7 +21,6 @@ import (
"fmt" "fmt"
"io" "io"
"math" "math"
"sort"
"strconv" "strconv"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
@ -144,6 +143,7 @@ func (l *promlexer) Error(es string) {
// Prometheus text exposition format. // Prometheus text exposition format.
type PromParser struct { type PromParser struct {
l *promlexer l *promlexer
builder labels.ScratchBuilder
series []byte series []byte
text []byte text []byte
mtype MetricType mtype MetricType
@ -212,14 +212,11 @@ func (p *PromParser) Comment() []byte {
// Metric writes the labels of the current sample into the passed labels. // Metric writes the labels of the current sample into the passed labels.
// It returns the string from which the metric was parsed. // It returns the string from which the metric was parsed.
func (p *PromParser) Metric(l *labels.Labels) string { func (p *PromParser) Metric(l *labels.Labels) string {
// Allocate the full immutable string immediately, so we just // Copy the buffer to a string: this is only necessary for the return value.
// have to create references on it below.
s := string(p.series) s := string(p.series)
*l = append(*l, labels.Label{ p.builder.Reset()
Name: labels.MetricName, p.builder.Add(labels.MetricName, s[:p.offsets[0]-p.start])
Value: s[:p.offsets[0]-p.start],
})
for i := 1; i < len(p.offsets); i += 4 { for i := 1; i < len(p.offsets); i += 4 {
a := p.offsets[i] - p.start a := p.offsets[i] - p.start
@ -227,16 +224,16 @@ func (p *PromParser) Metric(l *labels.Labels) string {
c := p.offsets[i+2] - p.start c := p.offsets[i+2] - p.start
d := p.offsets[i+3] - p.start d := p.offsets[i+3] - p.start
value := s[c:d]
// Replacer causes allocations. Replace only when necessary. // Replacer causes allocations. Replace only when necessary.
if strings.IndexByte(s[c:d], byte('\\')) >= 0 { if strings.IndexByte(s[c:d], byte('\\')) >= 0 {
*l = append(*l, labels.Label{Name: s[a:b], Value: lvalReplacer.Replace(s[c:d])}) value = lvalReplacer.Replace(value)
continue
} }
*l = append(*l, labels.Label{Name: s[a:b], Value: s[c:d]}) p.builder.Add(s[a:b], value)
} }
// Sort labels to maintain the sorted labels invariant. p.builder.Sort()
sort.Sort(*l) *l = p.builder.Labels()
return s return s
} }

View file

@ -19,7 +19,6 @@ import (
"fmt" "fmt"
"io" "io"
"math" "math"
"sort"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
@ -59,6 +58,8 @@ type ProtobufParser struct {
// that we have to decode the next MetricFamily. // that we have to decode the next MetricFamily.
state Entry state Entry
builder labels.ScratchBuilder // held here to reduce allocations when building Labels
mf *dto.MetricFamily mf *dto.MetricFamily
// The following are just shenanigans to satisfy the Parser interface. // The following are just shenanigans to satisfy the Parser interface.
@ -245,23 +246,19 @@ func (p *ProtobufParser) Comment() []byte {
// Metric writes the labels of the current sample into the passed labels. // Metric writes the labels of the current sample into the passed labels.
// It returns the string from which the metric was parsed. // It returns the string from which the metric was parsed.
func (p *ProtobufParser) Metric(l *labels.Labels) string { func (p *ProtobufParser) Metric(l *labels.Labels) string {
*l = append(*l, labels.Label{ p.builder.Reset()
Name: labels.MetricName, p.builder.Add(labels.MetricName, p.getMagicName())
Value: p.getMagicName(),
})
for _, lp := range p.mf.GetMetric()[p.metricPos].GetLabel() { for _, lp := range p.mf.GetMetric()[p.metricPos].GetLabel() {
*l = append(*l, labels.Label{ p.builder.Add(lp.GetName(), lp.GetValue())
Name: lp.GetName(),
Value: lp.GetValue(),
})
} }
if needed, name, value := p.getMagicLabel(); needed { if needed, name, value := p.getMagicLabel(); needed {
*l = append(*l, labels.Label{Name: name, Value: value}) p.builder.Add(name, value)
} }
// Sort labels to maintain the sorted labels invariant. // Sort labels to maintain the sorted labels invariant.
sort.Sort(*l) p.builder.Sort()
*l = p.builder.Labels()
return p.metricBytes.String() return p.metricBytes.String()
} }
@ -305,12 +302,12 @@ func (p *ProtobufParser) Exemplar(ex *exemplar.Exemplar) bool {
ex.HasTs = true ex.HasTs = true
ex.Ts = ts.GetSeconds()*1000 + int64(ts.GetNanos()/1_000_000) ex.Ts = ts.GetSeconds()*1000 + int64(ts.GetNanos()/1_000_000)
} }
p.builder.Reset()
for _, lp := range exProto.GetLabel() { for _, lp := range exProto.GetLabel() {
ex.Labels = append(ex.Labels, labels.Label{ p.builder.Add(lp.GetName(), lp.GetValue())
Name: lp.GetName(),
Value: lp.GetValue(),
})
} }
p.builder.Sort()
ex.Labels = p.builder.Labels()
return true return true
} }