Skip to content

Improve expfmt.Format with expfmt.NegotiateAccept ergonomics #993

Description

@bwplotka

Problem

  1. In PR #570, support for UTF-8 metric name escaping was introduced. This started to alter headers with extra parameters like escaping=allow-utf-8.
  2. Because format strings now had dynamic parameters (like ; escaping=...), direct string equality checks (if format == expfmt.FmtText) started failing in downstream code and tests. To stop users from doing string comparisons, PR #576: Unexport Format strings
    unexported all Fmt... constants (making them fmtText, fmtProtoDelim, etc.) and introduced FormatType enum (TypeTextPlain, TypeProtoDelim) along with expfmt.NewFormat(FormatType).
  3. Making things private immediately broke Kubernetes (k8s.io/component-base) and large portions of the Go Prometheus ecosystem. In response, PR #665: Revert #576 and add deprecation notice re-exported the constants, but marked every single one with Deprecated:. Since then community either updated to a NewFormat, or ignored the lint warnings.
  4. In feat: implement gauge and counter support for OpenMetrics 2.0 #894 we introduced NegotiateAccept(h, FmtProtoDelim, FmtProtoText, FmtProtoCompact, FmtText) that allows full granularity of choices to accept. We couldn't use FormatType because it does not encapsulate version.

As a result NegotiateAccept API is now quite hard to use.

Examples of the current usage:

expfmt.NegotiateAccept(req.Header, 
  expfmt.NewFormat(expfmt.TypeProtoDelim), expfmt.NewFormat(expfmt.TypeProtoText), expfmt.NewFormat(expfmt.TypeProtoCompact), expfmt.NewFormat(expfmt.TypeTextPlain)
  )
var (
        standardAcceptedFormats    []expfmt.Format
	openMetricsAcceptedFormats []expfmt.Format
)

func init() {
	standardAcceptedFormats = []expfmt.Format{
		expfmt.NewFormat(expfmt.TypeProtoDelim),
		expfmt.NewFormat(expfmt.TypeProtoText),
		expfmt.NewFormat(expfmt.TypeProtoCompact),
		expfmt.NewFormat(expfmt.TypeTextPlain),
	}

	om20Format, err := expfmt.NewOpenMetricsFormat(expfmt.OpenMetricsVersion_2_0_0)
	if err != nil {
		panic(err)
	}
	om10Format, err := expfmt.NewOpenMetricsFormat(expfmt.OpenMetricsVersion_1_0_0)
	if err != nil {
		panic(err)
	}
	om001Format, err := expfmt.NewOpenMetricsFormat(expfmt.OpenMetricsVersion_0_0_1)
	if err != nil {
		panic(err)
	}
	openMetricsAcceptedFormats = []expfmt.Format{
		om20Format,
		om10Format,
		om001Format,
		expfmt.NewFormat(expfmt.TypeProtoDelim),
		expfmt.NewFormat(expfmt.TypeProtoText),
		expfmt.NewFormat(expfmt.TypeProtoCompact),
		expfmt.NewFormat(expfmt.TypeTextPlain),
	}
}

Goal

  • NegotiateAccept is easier to use, especially on the hot path and for the known format sets

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions