Skip to content

Commit e7f4912

Browse files
ywwgbeorn7SuperQ
authored
expfmt: Add a way to generate different OpenMetrics Formats (#596)
* expfmt: Add a way to generate different OpenMetrics Formats Also complete test coverage of expfmt.go --------- Signed-off-by: Owen Williams <[email protected]> Signed-off-by: Björn Rabenstein <[email protected]> Co-authored-by: Björn Rabenstein <[email protected]> Co-authored-by: Ben Kochie <[email protected]>
1 parent d4cebf6 commit e7f4912

File tree

2 files changed

+145
-6
lines changed

2 files changed

+145
-6
lines changed

expfmt/expfmt.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package expfmt
1616

1717
import (
18+
"fmt"
1819
"strings"
1920

2021
"github.com/prometheus/common/model"
@@ -63,7 +64,7 @@ const (
6364
type FormatType int
6465

6566
const (
66-
TypeUnknown = iota
67+
TypeUnknown FormatType = iota
6768
TypeProtoCompact
6869
TypeProtoDelim
6970
TypeProtoText
@@ -73,7 +74,8 @@ const (
7374

7475
// NewFormat generates a new Format from the type provided. Mostly used for
7576
// tests, most Formats should be generated as part of content negotiation in
76-
// encode.go.
77+
// encode.go. If a type has more than one version, the latest version will be
78+
// returned.
7779
func NewFormat(t FormatType) Format {
7880
switch t {
7981
case TypeProtoCompact:
@@ -91,13 +93,21 @@ func NewFormat(t FormatType) Format {
9193
}
9294
}
9395

96+
// NewOpenMetricsFormat generates a new OpenMetrics format matching the
97+
// specified version number.
98+
func NewOpenMetricsFormat(version string) (Format, error) {
99+
if version == OpenMetricsVersion_0_0_1 {
100+
return fmtOpenMetrics_0_0_1, nil
101+
}
102+
if version == OpenMetricsVersion_1_0_0 {
103+
return fmtOpenMetrics_1_0_0, nil
104+
}
105+
return fmtUnknown, fmt.Errorf("unknown open metrics version string")
106+
}
107+
94108
// FormatType deduces an overall FormatType for the given format.
95109
func (f Format) FormatType() FormatType {
96110
toks := strings.Split(string(f), ";")
97-
if len(toks) < 2 {
98-
return TypeUnknown
99-
}
100-
101111
params := make(map[string]string)
102112
for i, t := range toks {
103113
if i == 0 {

expfmt/expfmt_test.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright 2024 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package expfmt
15+
16+
import (
17+
"testing"
18+
19+
"github.com/prometheus/common/model"
20+
)
21+
22+
// Test Format to Escapting Scheme conversion
23+
// Path: expfmt/expfmt_test.go
24+
// Compare this snippet from expfmt/expfmt.go:
25+
func TestToFormatType(t *testing.T) {
26+
tests := []struct {
27+
format Format
28+
expected FormatType
29+
}{
30+
{
31+
format: fmtProtoCompact,
32+
expected: TypeProtoCompact,
33+
},
34+
{
35+
format: fmtProtoDelim,
36+
expected: TypeProtoDelim,
37+
},
38+
{
39+
format: fmtProtoText,
40+
expected: TypeProtoText,
41+
},
42+
{
43+
format: fmtOpenMetrics_1_0_0,
44+
expected: TypeOpenMetrics,
45+
},
46+
{
47+
format: fmtText,
48+
expected: TypeTextPlain,
49+
},
50+
{
51+
format: fmtOpenMetrics_0_0_1,
52+
expected: TypeOpenMetrics,
53+
},
54+
{
55+
format: "application/vnd.google.protobuf; proto=BadProtocol; encoding=text",
56+
expected: TypeUnknown,
57+
},
58+
{
59+
format: "application/vnd.google.protobuf",
60+
expected: TypeUnknown,
61+
},
62+
{
63+
format: "application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily=bad",
64+
expected: TypeUnknown,
65+
},
66+
// encoding missing
67+
{
68+
format: "application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily",
69+
expected: TypeUnknown,
70+
},
71+
// invalid encoding
72+
{
73+
format: "application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=textual",
74+
expected: TypeUnknown,
75+
},
76+
// bad charset, must be utf-8
77+
{
78+
format: "application/openmetrics-text; version=1.0.0; charset=ascii",
79+
expected: TypeUnknown,
80+
},
81+
{
82+
format: "text/plain",
83+
expected: TypeTextPlain,
84+
},
85+
{
86+
format: "text/plain; version=invalid",
87+
expected: TypeUnknown,
88+
},
89+
{
90+
format: "gobbledygook",
91+
expected: TypeUnknown,
92+
},
93+
}
94+
for _, test := range tests {
95+
if test.format.FormatType() != test.expected {
96+
t.Errorf("expected %v got %v", test.expected, test.format.FormatType())
97+
}
98+
}
99+
}
100+
101+
func TestToEscapingScheme(t *testing.T) {
102+
tests := []struct {
103+
format Format
104+
expected model.EscapingScheme
105+
}{
106+
{
107+
format: fmtProtoCompact,
108+
expected: model.ValueEncodingEscaping,
109+
},
110+
{
111+
format: "application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=underscores",
112+
expected: model.UnderscoreEscaping,
113+
},
114+
{
115+
format: "application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=allow-utf-8",
116+
expected: model.NoEscaping,
117+
},
118+
// error returns default
119+
{
120+
format: "application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=invalid",
121+
expected: model.NameEscapingScheme,
122+
},
123+
}
124+
for _, test := range tests {
125+
if test.format.ToEscapingScheme() != test.expected {
126+
t.Errorf("expected %v got %v", test.expected, test.format.ToEscapingScheme())
127+
}
128+
}
129+
}

0 commit comments

Comments
 (0)