This commit is contained in:
Grégoire 2025-08-05 18:11:56 -03:00 committed by GitHub
commit 3945b525e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 48 additions and 20 deletions

View File

@ -297,6 +297,9 @@ func (c *flagConfig) setFeatureListOptions(logger *slog.Logger) error {
case "use-uncached-io":
c.tsdb.UseUncachedIO = true
logger.Info("Experimental Uncached IO is enabled.")
case "otel-suffix":
c.scrape.EnableOtelSuffix = true
logger.Info("Experimental otel suffix is enabled.")
default:
logger.Warn("Unknown option for --enable-feature", "option", o)
}

View File

@ -149,7 +149,7 @@ func benchParse(b *testing.B, data []byte, parser string) {
}
case "promproto":
newParserFn = func(b []byte, st *labels.SymbolTable) Parser {
return NewProtobufParser(b, true, false, st)
return NewProtobufParser(b, true, false, false, st)
}
case "omtext":
newParserFn = func(b []byte, st *labels.SymbolTable) Parser {
@ -275,7 +275,7 @@ func BenchmarkCreatedTimestampPromProto(b *testing.B) {
data := createTestProtoBuf(b).Bytes()
st := labels.NewSymbolTable()
p := NewProtobufParser(data, true, false, st)
p := NewProtobufParser(data, true, false, false, st)
found := false
Inner:

View File

@ -130,7 +130,7 @@ func extractMediaType(contentType, fallbackType string) (string, error) {
// An error may also be returned if fallbackType had to be used or there was some
// other error parsing the supplied Content-Type.
// If the returned parser is nil then the scrape must fail.
func New(b []byte, contentType, fallbackType string, parseClassicHistograms, skipOMCTSeries, enableTypeAndUnitLabels bool, st *labels.SymbolTable) (Parser, error) {
func New(b []byte, contentType, fallbackType string, parseClassicHistograms, skipOMCTSeries, enableTypeAndUnitLabels bool, enableOtelSuffix bool, st *labels.SymbolTable) (Parser, error) {
mediaType, err := extractMediaType(contentType, fallbackType)
// err may be nil or something we want to warn about.
@ -139,9 +139,10 @@ func New(b []byte, contentType, fallbackType string, parseClassicHistograms, ski
return NewOpenMetricsParser(b, st, func(o *openMetricsParserOptions) {
o.skipCTSeries = skipOMCTSeries
o.enableTypeAndUnitLabels = enableTypeAndUnitLabels
o.enableOtelSuffix = enableOtelSuffix
}), err
case "application/vnd.google.protobuf":
return NewProtobufParser(b, parseClassicHistograms, enableTypeAndUnitLabels, st), err
return NewProtobufParser(b, parseClassicHistograms, enableTypeAndUnitLabels, enableOtelSuffix, st), err
case "text/plain":
return NewPromParser(b, st, enableTypeAndUnitLabels), err
default:

View File

@ -168,7 +168,7 @@ func TestNewParser(t *testing.T) {
fallbackProtoMediaType := tt.fallbackScrapeProtocol.HeaderMediaType()
p, err := New([]byte{}, tt.contentType, fallbackProtoMediaType, false, false, false, labels.NewSymbolTable())
p, err := New([]byte{}, tt.contentType, fallbackProtoMediaType, false, false, false, false, labels.NewSymbolTable())
tt.validateParser(t, p)
if tt.err == "" {
require.NoError(t, err)

View File

@ -598,7 +598,7 @@ func TestNHCBParser_NoNHCBWhenExponential(t *testing.T) {
func() (string, parserFactory, []int, parserOptions) {
factory := func(keepClassic bool) Parser {
inputBuf := createTestProtoBufHistogram(t)
return NewProtobufParser(inputBuf.Bytes(), keepClassic, false, labels.NewSymbolTable())
return NewProtobufParser(inputBuf.Bytes(), keepClassic, false, false, labels.NewSymbolTable())
}
return "ProtoBuf", factory, []int{1, 2, 3}, parserOptions{useUTF8sep: true, hasCreatedTimeStamp: true}
},
@ -1121,7 +1121,7 @@ metric: <
},
}
p := NewProtobufParser(buf.Bytes(), false, false, labels.NewSymbolTable())
p := NewProtobufParser(buf.Bytes(), false, false, false, labels.NewSymbolTable())
p = NewNHCBParser(p, labels.NewSymbolTable(), false)
got := testParse(t, p)
requireEntries(t, exp, got)

View File

@ -112,11 +112,13 @@ type OpenMetricsParser struct {
visitedMFName []byte
skipCTSeries bool
enableTypeAndUnitLabels bool
enableOtelSuffix bool
}
type openMetricsParserOptions struct {
skipCTSeries bool
enableTypeAndUnitLabels bool
enableOtelSuffix bool
}
type OpenMetricsOption func(*openMetricsParserOptions)
@ -155,6 +157,7 @@ func NewOpenMetricsParser(b []byte, st *labels.SymbolTable, opts ...OpenMetricsO
builder: labels.NewScratchBuilderWithSymbolTable(st, 16),
skipCTSeries: options.skipCTSeries,
enableTypeAndUnitLabels: options.enableTypeAndUnitLabels,
enableOtelSuffix: options.enableOtelSuffix,
}
return parser
@ -524,7 +527,7 @@ func (p *OpenMetricsParser) Next() (Entry, error) {
case tUnit:
p.unit = string(p.text)
m := yoloString(p.l.b[p.offsets[0]:p.offsets[1]])
if len(p.unit) > 0 {
if len(p.unit) > 0 && !p.enableOtelSuffix {
if !strings.HasSuffix(m, p.unit) || len(m) < len(p.unit)+1 || p.l.b[p.offsets[1]-len(p.unit)-1] != '_' {
return EntryInvalid, fmt.Errorf("unit %q not a suffix of metric %q", p.unit, m)
}

View File

@ -80,10 +80,12 @@ type ProtobufParser struct {
// native histogram.
parseClassicHistograms bool
enableTypeAndUnitLabels bool
enableOtelSuffix bool
}
// NewProtobufParser returns a parser for the payload in the byte slice.
func NewProtobufParser(b []byte, parseClassicHistograms, enableTypeAndUnitLabels bool, st *labels.SymbolTable) Parser {
func NewProtobufParser(b []byte, parseClassicHistograms, enableTypeAndUnitLabels bool, enableOtelSuffix bool, st *labels.SymbolTable) Parser {
return &ProtobufParser{
dec: dto.NewMetricStreamingDecoder(b),
entryBytes: &bytes.Buffer{},
@ -92,6 +94,7 @@ func NewProtobufParser(b []byte, parseClassicHistograms, enableTypeAndUnitLabels
state: EntryInvalid,
parseClassicHistograms: parseClassicHistograms,
enableTypeAndUnitLabels: enableTypeAndUnitLabels,
enableOtelSuffix: enableOtelSuffix,
}
}
@ -446,7 +449,7 @@ func (p *ProtobufParser) Next() (Entry, error) {
return EntryInvalid, fmt.Errorf("unknown metric type for metric %q: %s", name, p.dec.GetType())
}
unit := p.dec.GetUnit()
if len(unit) > 0 {
if len(unit) > 0 && !p.enableOtelSuffix {
if p.dec.GetType() == dto.MetricType_COUNTER && strings.HasSuffix(name, "_total") {
if !strings.HasSuffix(name[:len(name)-6], unit) || len(name)-6 < len(unit)+1 || name[len(name)-6-len(unit)-1] != '_' {
return EntryInvalid, fmt.Errorf("unit %q not a suffix of counter %q", unit, name)
@ -609,12 +612,20 @@ func (p *ProtobufParser) getMagicName() string {
return p.dec.GetName()
}
if p.fieldPos == -2 {
return p.dec.GetName() + "_count"
if p.enableOtelSuffix {
return p.dec.GetName() + ".count"
} else {
return p.dec.GetName() + "_count"
}
}
if p.fieldPos == -1 {
return p.dec.GetName() + "_sum"
if p.enableOtelSuffix {
return p.dec.GetName() + ".sum"
} else {
return p.dec.GetName() + "_sum"
}
}
if t == dto.MetricType_HISTOGRAM || t == dto.MetricType_GAUGE_HISTOGRAM {
if !p.enableOtelSuffix && (t == dto.MetricType_HISTOGRAM || t == dto.MetricType_GAUGE_HISTOGRAM) {
return p.dec.GetName() + "_bucket"
}
return p.dec.GetName()

View File

@ -833,7 +833,7 @@ func TestProtobufParse(t *testing.T) {
}{
{
name: "parseClassicHistograms=false/enableTypeAndUnitLabels=false",
parser: NewProtobufParser(inputBuf.Bytes(), false, false, labels.NewSymbolTable()),
parser: NewProtobufParser(inputBuf.Bytes(), false, false, false, labels.NewSymbolTable()),
expected: []parsedEntry{
{
m: "go_build_info",
@ -1468,7 +1468,7 @@ func TestProtobufParse(t *testing.T) {
},
{
name: "parseClassicHistograms=false/enableTypeAndUnitLabels=true",
parser: NewProtobufParser(inputBuf.Bytes(), false, true, labels.NewSymbolTable()),
parser: NewProtobufParser(inputBuf.Bytes(), false, true, false, labels.NewSymbolTable()),
expected: []parsedEntry{
{
m: "go_build_info",
@ -2140,7 +2140,7 @@ func TestProtobufParse(t *testing.T) {
},
{
name: "parseClassicHistograms=true/enableTypeAndUnitLabels=false",
parser: NewProtobufParser(inputBuf.Bytes(), true, false, labels.NewSymbolTable()),
parser: NewProtobufParser(inputBuf.Bytes(), true, false, false, labels.NewSymbolTable()),
expected: []parsedEntry{
{
m: "go_build_info",

View File

@ -61,7 +61,7 @@ const (
var symbolTable = labels.NewSymbolTable()
func fuzzParseMetricWithContentType(in []byte, contentType string) int {
p, warning := textparse.New(in, contentType, "", false, false, false, symbolTable)
p, warning := textparse.New(in, contentType, "", false, false, false, false, symbolTable)
if p == nil || warning != nil {
// An invalid content type is being passed, which should not happen
// in this context.

View File

@ -92,6 +92,9 @@ type Options struct {
// EnableTypeAndUnitLabels
EnableTypeAndUnitLabels bool
// Allows metric names not to be suffixed with their unit and use dots for magic suffix (e.g. ".sum").
EnableOtelSuffix bool
// Optional HTTP client options to use when scraping.
HTTPClientOptions []config_util.HTTPClientOption

View File

@ -211,6 +211,7 @@ func newScrapePool(cfg *config.ScrapeConfig, app storage.Appendable, offsetSeed
options.EnableNativeHistogramsIngestion,
options.EnableCreatedTimestampZeroIngestion,
options.EnableTypeAndUnitLabels,
options.EnableOtelSuffix,
options.ExtraMetrics,
options.AppendMetadata,
opts.target,
@ -933,6 +934,7 @@ type scrapeLoop struct {
enableNativeHistogramIngestion bool
enableCTZeroIngestion bool
enableTypeAndUnitLabels bool
enableOtelSuffix bool
appender func(ctx context.Context) storage.Appender
symbolTable *labels.SymbolTable
@ -1241,6 +1243,7 @@ func newScrapeLoop(ctx context.Context,
enableNativeHistogramIngestion bool,
enableCTZeroIngestion bool,
enableTypeAndUnitLabels bool,
enableOtelSuffix bool,
reportExtraMetrics bool,
appendMetadataToWAL bool,
target *Target,
@ -1299,6 +1302,7 @@ func newScrapeLoop(ctx context.Context,
enableNativeHistogramIngestion: enableNativeHistogramIngestion,
enableCTZeroIngestion: enableCTZeroIngestion,
enableTypeAndUnitLabels: enableTypeAndUnitLabels,
enableOtelSuffix: enableOtelSuffix,
reportExtraMetrics: reportExtraMetrics,
appendMetadataToWAL: appendMetadataToWAL,
metrics: metrics,
@ -1625,7 +1629,7 @@ func (sl *scrapeLoop) append(app storage.Appender, b []byte, contentType string,
return
}
p, err := textparse.New(b, contentType, sl.fallbackScrapeProtocol, sl.alwaysScrapeClassicHist, sl.enableCTZeroIngestion, sl.enableTypeAndUnitLabels, sl.symbolTable)
p, err := textparse.New(b, contentType, sl.fallbackScrapeProtocol, sl.alwaysScrapeClassicHist, sl.enableCTZeroIngestion, sl.enableTypeAndUnitLabels, sl.enableOtelSuffix, sl.symbolTable)
if p == nil {
sl.l.Error(
"Failed to determine correct type of scrape target.",

View File

@ -986,6 +986,7 @@ func newBasicScrapeLoopWithFallback(t testing.TB, ctx context.Context, scraper s
false,
false,
false,
false,
true,
nil,
false,
@ -1135,6 +1136,7 @@ func TestScrapeLoopRun(t *testing.T) {
false,
false,
false,
false,
nil,
false,
scrapeMetrics,
@ -1284,6 +1286,7 @@ func TestScrapeLoopMetadata(t *testing.T) {
false,
false,
false,
false,
nil,
false,
scrapeMetrics,
@ -2011,7 +2014,7 @@ func TestScrapeLoopAppendCacheEntryButErrNotFound(t *testing.T) {
fakeRef := storage.SeriesRef(1)
expValue := float64(1)
metric := []byte(`metric{n="1"} 1`)
p, warning := textparse.New(metric, "text/plain", "", false, false, false, labels.NewSymbolTable())
p, warning := textparse.New(metric, "text/plain", "", false, false, false, false, labels.NewSymbolTable())
require.NotNil(t, p)
require.NoError(t, warning)

View File

@ -392,7 +392,7 @@ func TestFederationWithNativeHistograms(t *testing.T) {
require.Equal(t, http.StatusOK, res.Code)
body, err := io.ReadAll(res.Body)
require.NoError(t, err)
p := textparse.NewProtobufParser(body, false, false, labels.NewSymbolTable())
p := textparse.NewProtobufParser(body, false, false, false, labels.NewSymbolTable())
var actVec promql.Vector
metricFamilies := 0
l := labels.Labels{}