Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions expfmt/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,21 @@ func Negotiate(h http.Header) Format {
// temporary and will disappear once FmtOpenMetrics is fully supported and as
// such may be negotiated by the normal Negotiate function.
func NegotiateIncludingOpenMetrics(h http.Header) Format {
formats := NegotiateFormatsIncludingOpenMetrics(h)
return formats[0]
}

// NegotiateFormatsIncludingOpenMetrics works like NegotiateIncludingOpenMetrics
// but returns all recognised formats from the Accept header in descending
// preference order instead of stopping at the first match. If no recognised
// formats are found, the slice contains FmtText (with the negotiated escaping
// scheme) as the default fallback.
//
// Callers that only support a subset of formats can iterate the result and pick
// the first format they can serve.
func NegotiateFormatsIncludingOpenMetrics(h http.Header) []Format {
escapingScheme := Format(fmt.Sprintf("; escaping=%s", Format(model.NameEscapingScheme.String())))
var formats []Format
for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
if escapeParam := ac.Params[model.EscapingKey]; escapeParam != "" {
switch Format(escapeParam) {
Expand All @@ -108,26 +122,31 @@ func NegotiateIncludingOpenMetrics(h http.Header) Format {
if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
switch ac.Params["encoding"] {
case "delimited":
return FmtProtoDelim + escapingScheme
formats = append(formats, FmtProtoDelim+escapingScheme)
case "text":
return FmtProtoText + escapingScheme
formats = append(formats, FmtProtoText+escapingScheme)
case "compact-text":
return FmtProtoCompact + escapingScheme
formats = append(formats, FmtProtoCompact+escapingScheme)
}
continue
}
if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
return FmtText + escapingScheme
formats = append(formats, FmtText+escapingScheme)
continue
}
if ac.Type+"/"+ac.SubType == OpenMetricsType && (ver == OpenMetricsVersion_0_0_1 || ver == OpenMetricsVersion_1_0_0 || ver == "") {
switch ver {
case OpenMetricsVersion_1_0_0:
return FmtOpenMetrics_1_0_0 + escapingScheme
formats = append(formats, FmtOpenMetrics_1_0_0+escapingScheme)
default:
return FmtOpenMetrics_0_0_1 + escapingScheme
formats = append(formats, FmtOpenMetrics_0_0_1+escapingScheme)
}
}
}
return FmtText + escapingScheme
if len(formats) == 0 {
return []Format{FmtText + escapingScheme}
}
return formats
}

// NewEncoder returns a new encoder based on content type negotiation. All
Expand Down
30 changes: 30 additions & 0 deletions expfmt/encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,36 @@ func TestNegotiateIncludingOpenMetrics(t *testing.T) {
}
}

func TestNegotiateFormatsIncludingOpenMetrics(t *testing.T) {
oldDefault := model.NameEscapingScheme
model.NameEscapingScheme = model.ValueEncodingEscaping
defer func() {
model.NameEscapingScheme = oldDefault
}()

h := http.Header{}
h.Add(hdrAccept, "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.6,application/openmetrics-text;version=1.0.0;escaping=allow-utf-8;q=0.5,text/plain;version=0.0.4;q=0.2")

got := NegotiateFormatsIncludingOpenMetrics(h)
want := []Format{
FmtProtoDelim + "; escaping=values",
FmtOpenMetrics_1_0_0 + "; escaping=allow-utf-8",
FmtText + "; escaping=allow-utf-8",
}
if len(got) != len(want) {
t.Fatalf("expected %d formats, got %d: %#v", len(want), len(got), got)
}
for i := range want {
if got[i] != want[i] {
t.Errorf("format[%d]: want %q, got %q", i, want[i], got[i])
}
}
// First entry must match NegotiateIncludingOpenMetrics.
if NegotiateIncludingOpenMetrics(h) != got[0] {
t.Errorf("NegotiateIncludingOpenMetrics should return first negotiated format")
}
}

func TestEncode(t *testing.T) {
metric1 := &dto.MetricFamily{
Name: proto.String("foo_metric"),
Expand Down
Loading