From 44e573ec9a6f42490c627f9e5e3b189a51e24eaf Mon Sep 17 00:00:00 2001 From: prombot Date: Wed, 13 Mar 2024 17:48:14 +0000 Subject: [PATCH 1/4] Update common Prometheus files Signed-off-by: prombot --- Makefile.common | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile.common b/Makefile.common index 92558151e..49ed5f547 100644 --- a/Makefile.common +++ b/Makefile.common @@ -208,6 +208,10 @@ common-tarball: promu @echo ">> building release tarball" $(PROMU) tarball --prefix $(PREFIX) $(BIN_DIR) +.PHONY: common-docker-repo-name +common-docker-repo-name: + @echo "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)" + .PHONY: common-docker $(BUILD_DOCKER_ARCHS) common-docker: $(BUILD_DOCKER_ARCHS) $(BUILD_DOCKER_ARCHS): common-docker-%: From e7f4912c3e705829fb6c214812fb71ced44ec948 Mon Sep 17 00:00:00 2001 From: Owen Williams Date: Thu, 14 Mar 2024 14:36:17 -0400 Subject: [PATCH 2/4] expfmt: Add a way to generate different OpenMetrics Formats (#596) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * expfmt: Add a way to generate different OpenMetrics Formats Also complete test coverage of expfmt.go --------- Signed-off-by: Owen Williams Signed-off-by: Björn Rabenstein Co-authored-by: Björn Rabenstein Co-authored-by: Ben Kochie --- expfmt/expfmt.go | 22 +++++-- expfmt/expfmt_test.go | 129 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 6 deletions(-) create mode 100644 expfmt/expfmt_test.go diff --git a/expfmt/expfmt.go b/expfmt/expfmt.go index 6fc9555e3..051b38cd1 100644 --- a/expfmt/expfmt.go +++ b/expfmt/expfmt.go @@ -15,6 +15,7 @@ package expfmt import ( + "fmt" "strings" "github.com/prometheus/common/model" @@ -63,7 +64,7 @@ const ( type FormatType int const ( - TypeUnknown = iota + TypeUnknown FormatType = iota TypeProtoCompact TypeProtoDelim TypeProtoText @@ -73,7 +74,8 @@ const ( // NewFormat generates a new Format from the type provided. Mostly used for // tests, most Formats should be generated as part of content negotiation in -// encode.go. +// encode.go. If a type has more than one version, the latest version will be +// returned. func NewFormat(t FormatType) Format { switch t { case TypeProtoCompact: @@ -91,13 +93,21 @@ func NewFormat(t FormatType) Format { } } +// NewOpenMetricsFormat generates a new OpenMetrics format matching the +// specified version number. +func NewOpenMetricsFormat(version string) (Format, error) { + if version == OpenMetricsVersion_0_0_1 { + return fmtOpenMetrics_0_0_1, nil + } + if version == OpenMetricsVersion_1_0_0 { + return fmtOpenMetrics_1_0_0, nil + } + return fmtUnknown, fmt.Errorf("unknown open metrics version string") +} + // FormatType deduces an overall FormatType for the given format. func (f Format) FormatType() FormatType { toks := strings.Split(string(f), ";") - if len(toks) < 2 { - return TypeUnknown - } - params := make(map[string]string) for i, t := range toks { if i == 0 { diff --git a/expfmt/expfmt_test.go b/expfmt/expfmt_test.go new file mode 100644 index 000000000..8ec16524b --- /dev/null +++ b/expfmt/expfmt_test.go @@ -0,0 +1,129 @@ +// Copyright 2024 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package expfmt + +import ( + "testing" + + "github.com/prometheus/common/model" +) + +// Test Format to Escapting Scheme conversion +// Path: expfmt/expfmt_test.go +// Compare this snippet from expfmt/expfmt.go: +func TestToFormatType(t *testing.T) { + tests := []struct { + format Format + expected FormatType + }{ + { + format: fmtProtoCompact, + expected: TypeProtoCompact, + }, + { + format: fmtProtoDelim, + expected: TypeProtoDelim, + }, + { + format: fmtProtoText, + expected: TypeProtoText, + }, + { + format: fmtOpenMetrics_1_0_0, + expected: TypeOpenMetrics, + }, + { + format: fmtText, + expected: TypeTextPlain, + }, + { + format: fmtOpenMetrics_0_0_1, + expected: TypeOpenMetrics, + }, + { + format: "application/vnd.google.protobuf; proto=BadProtocol; encoding=text", + expected: TypeUnknown, + }, + { + format: "application/vnd.google.protobuf", + expected: TypeUnknown, + }, + { + format: "application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily=bad", + expected: TypeUnknown, + }, + // encoding missing + { + format: "application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily", + expected: TypeUnknown, + }, + // invalid encoding + { + format: "application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=textual", + expected: TypeUnknown, + }, + // bad charset, must be utf-8 + { + format: "application/openmetrics-text; version=1.0.0; charset=ascii", + expected: TypeUnknown, + }, + { + format: "text/plain", + expected: TypeTextPlain, + }, + { + format: "text/plain; version=invalid", + expected: TypeUnknown, + }, + { + format: "gobbledygook", + expected: TypeUnknown, + }, + } + for _, test := range tests { + if test.format.FormatType() != test.expected { + t.Errorf("expected %v got %v", test.expected, test.format.FormatType()) + } + } +} + +func TestToEscapingScheme(t *testing.T) { + tests := []struct { + format Format + expected model.EscapingScheme + }{ + { + format: fmtProtoCompact, + expected: model.ValueEncodingEscaping, + }, + { + format: "application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=underscores", + expected: model.UnderscoreEscaping, + }, + { + format: "application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=allow-utf-8", + expected: model.NoEscaping, + }, + // error returns default + { + format: "application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=invalid", + expected: model.NameEscapingScheme, + }, + } + for _, test := range tests { + if test.format.ToEscapingScheme() != test.expected { + t.Errorf("expected %v got %v", test.expected, test.format.ToEscapingScheme()) + } + } +} From b0624a8b0210bebed817d8cc0f001758cc6ece16 Mon Sep 17 00:00:00 2001 From: Brad Cowie Date: Thu, 21 Mar 2024 21:25:00 +1300 Subject: [PATCH 3/4] Fix string slice definition for FormatFlagOptions. (#607) Signed-off-by: Brad Cowie --- promlog/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/promlog/log.go b/promlog/log.go index 2449c90fb..50746544a 100644 --- a/promlog/log.go +++ b/promlog/log.go @@ -36,7 +36,7 @@ var ( ) LevelFlagOptions = []string{"debug", "info", "warn", "error"} - FormatFlagOptions = []string{"logfmt, json"} + FormatFlagOptions = []string{"logfmt", "json"} ) // AllowedLevel is a settable identifier for the minimum level a log entry From da75ecdeacfc67c4929f2cb874a9c536a81a8c07 Mon Sep 17 00:00:00 2001 From: Arianna Vespri Date: Thu, 21 Mar 2024 11:14:49 +0100 Subject: [PATCH 4/4] Correct logic in sample naming for counters, add new test Signed-off-by: Arianna Vespri --- expfmt/openmetrics_create.go | 6 ++-- expfmt/openmetrics_create_test.go | 49 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/expfmt/openmetrics_create.go b/expfmt/openmetrics_create.go index 432843da3..353c5e93f 100644 --- a/expfmt/openmetrics_create.go +++ b/expfmt/openmetrics_create.go @@ -248,12 +248,12 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily, options ...E var createdTsBytesWritten int // Finally the samples, one line for each. + if metricType == dto.MetricType_COUNTER && strings.HasSuffix(name, "_total") { + compliantName = compliantName + "_total" + } for _, metric := range in.Metric { switch metricType { case dto.MetricType_COUNTER: - if strings.HasSuffix(name, "_total") { - compliantName = compliantName + "_total" - } if metric.Counter == nil { return written, fmt.Errorf( "expected counter in metric %s %s", compliantName, metric, diff --git a/expfmt/openmetrics_create_test.go b/expfmt/openmetrics_create_test.go index 4a68eff39..58b284e9e 100644 --- a/expfmt/openmetrics_create_test.go +++ b/expfmt/openmetrics_create_test.go @@ -694,6 +694,55 @@ request_duration_microseconds_count 2693 options: []EncoderOption{WithUnit()}, out: `# HELP name doc string # TYPE name counter +`, + }, + // 18: Counter, timestamp given, unit opted in, _total suffix. + { + in: &dto.MetricFamily{ + Name: proto.String("some_measure_total"), + Help: proto.String("some testing measurement"), + Type: dto.MetricType_COUNTER.Enum(), + Unit: proto.String("seconds"), + Metric: []*dto.Metric{ + { + Label: []*dto.LabelPair{ + { + Name: proto.String("labelname"), + Value: proto.String("val1"), + }, + { + Name: proto.String("basename"), + Value: proto.String("basevalue"), + }, + }, + Counter: &dto.Counter{ + Value: proto.Float64(42), + }, + }, + { + Label: []*dto.LabelPair{ + { + Name: proto.String("labelname"), + Value: proto.String("val2"), + }, + { + Name: proto.String("basename"), + Value: proto.String("basevalue"), + }, + }, + Counter: &dto.Counter{ + Value: proto.Float64(.23), + }, + TimestampMs: proto.Int64(1234567890), + }, + }, + }, + options: []EncoderOption{WithUnit()}, + out: `# HELP some_measure_seconds some testing measurement +# TYPE some_measure_seconds counter +# UNIT some_measure_seconds seconds +some_measure_seconds_total{labelname="val1",basename="basevalue"} 42.0 +some_measure_seconds_total{labelname="val2",basename="basevalue"} 0.23 1.23456789e+06 `, }, }