Skip to content

Commit

Permalink
expfmt: Add a way to generate different OpenMetrics Formats (#596)
Browse files Browse the repository at this point in the history
* expfmt: Add a way to generate different OpenMetrics Formats

Also complete test coverage of expfmt.go

---------

Signed-off-by: Owen Williams <owen.williams@grafana.com>
Signed-off-by: Björn Rabenstein <github@rabenste.in>
Co-authored-by: Björn Rabenstein <github@rabenste.in>
Co-authored-by: Ben Kochie <superq@gmail.com>
  • Loading branch information
3 people committed Mar 14, 2024
1 parent d4cebf6 commit e7f4912
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 6 deletions.
22 changes: 16 additions & 6 deletions expfmt/expfmt.go
Expand Up @@ -15,6 +15,7 @@
package expfmt

import (
"fmt"
"strings"

"github.com/prometheus/common/model"
Expand Down Expand Up @@ -63,7 +64,7 @@ const (
type FormatType int

const (
TypeUnknown = iota
TypeUnknown FormatType = iota
TypeProtoCompact
TypeProtoDelim
TypeProtoText
Expand All @@ -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:
Expand All @@ -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 {
Expand Down
129 changes: 129 additions & 0 deletions 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())
}
}
}

0 comments on commit e7f4912

Please sign in to comment.