Skip to content

Experimental OpenMetrics 2.0 support #1912

@zeitlinger

Description

@zeitlinger

The OpenMetrics 2.0 spec is nearing completion. We should start implementing experimental support to validate the design and provide early feedback to the spec authors.

All features are behind opt-in flags (off by default) so there's no risk to existing users. The spec is still in draft, so the API is explicitly experimental and subject to change.

Flag design

Top-level gate + individual feature flags. The top-level gate (enableOpenMetrics2())
has no behavioral effect by itself — it just enables the sub-flags. Each sub-flag
independently controls one OM2 feature.

Java builder API:

PrometheusConfig config = PrometheusConfig.builder()
    .enableOpenMetrics2(om2 -> om2
        .contentNegotiation(true)         // version=2.0.0 content type
        .disableSuffixAppending(true)     // _total, unit suffixes
        .compositeValues(true)            // single-line histogram/summary + st@
        .exemplarCompliance(true)         // mandatory timestamps, no size limit
    )
    .build();

// Or enable everything at once
PrometheusConfig config = PrometheusConfig.builder()
    .enableOpenMetrics2(om2 -> om2.enableAll())
    .build();

Properties equivalent:

io.prometheus.openMetrics2.contentNegotiation=true
io.prometheus.openMetrics2.disableSuffixAppending=true
io.prometheus.openMetrics2.compositeValues=true
io.prometheus.openMetrics2.exemplarCompliance=true
Flag Description Spec reference
contentNegotiation Gate OM2 features behind content negotiation: only apply them when the scraper requests version=2.0.0, and return OM1 format when the scraper requests OM1. Without this flag, OM2 features are applied even if the scraper requests OM1 Content Type
disableSuffixAppending Don't append _total (Counter) or unit suffixes. OM2 downgrades these from MUST to SHOULD. This flag will remain useful even after OM2 is stable, since OM2 still says SHOULD (not MUST NOT) for these suffixes Counter, MetricFamily
compositeValues Single-line Histogram/Summary/GaugeHistogram with inline st@ start timestamp. Also: reserved label prefix __, Histogram Count/Sum always present, bucket values may be float CompositeValue, Overall Structure
exemplarCompliance Exemplar timestamps always emitted (MUST in OM2), 128-char LabelSet hard limit removed Exemplar
nativeHistograms Exponential buckets with schema, zero threshold/count, pos/neg spans Native Buckets

All flags default to false.

PR structure

PR 0: Flag infrastructure
│     enableOpenMetrics2() builder, OpenMetrics2Config class,
│     properties parsing — no behavioral changes
│
├── PR 1: disableSuffixAppending + contentNegotiation
│
├── PR 2: compositeValues + exemplarCompliance
│
└── PR 3: nativeHistograms

PRs 1–3 all depend only on PR 0 and can be done in parallel.

PR 0: Flag infrastructure

  • Add enableOpenMetrics2() to PrometheusConfig.Builder
  • Add OpenMetrics2Config class with all flag fields (defaulting to false)
  • Wire up properties parsing for io.prometheus.openMetrics2.*
  • No behavioral changes — just the config plumbing

PR 1: Suffix relaxation + content negotiation

  • disableSuffixAppending: suppress _total suffix on Counters, suppress unit suffix on metric names
  • contentNegotiation: only return OM2 format when the scraper explicitly requests version=2.0.0. Without this flag, OM2 features are applied even if the scraper requests OM1
  • Spec: Counter _total — "SHOULD have the suffix _total" (was MUST in OM1). Unit — "SHOULD be a suffix" (was MUST in OM1)

PR 2: CompositeValue + start timestamp + exemplar compliance

  • compositeValues:
    • Histogram as single line: foo {count:17,sum:324789.3,bucket:[0.1:8,0.25:10,0.5:11,1.0:14,+Inf:17]} 1.0 st@0.5
    • Summary as single line: bar {count:17,sum:324789.3,quantile:[0.95:123.7,0.99:150.0]}
    • GaugeHistogram as single line with count/sum (not gcount/gsum)
    • _created lines replaced by inline st@ start timestamp
    • _count, _sum, _bucket suffixed lines no longer emitted for these types
    • Reserved label prefix changed from _ to __
    • Histogram/GaugeHistogram Count and Sum always present (MUST, was SHOULD for Sum)
    • Histogram bucket values may be float (SHOULD be integer, was MUST)
    • Spec: CompositeValue, Histogram, Summary
  • exemplarCompliance:
    • Exemplar timestamps always emitted (OM2: MUST, was MAY in OM1)
    • 128-char LabelSet hard limit removed

PR 3: Native histograms

  • nativeHistograms:
    • Exponential bucket model: schema (-4 to 8), zero threshold/count, positive/negative spans and buckets
    • Text serialization: {count:X,sum:X,schema:N,zero_threshold:F,zero_count:X,positive_spans:[...],positive_buckets:[...]}
    • Can coexist with classic buckets on separate lines (native line MUST come first)
    • GaugeHistogram also supports native buckets (same syntax)
    • Multiple exemplars per native histogram sample
    • Spec: Native Buckets, Histogram
  • No known blockers:

Notes

  • UTF-8 metric/label names are already supported (shipped in a previous release)
  • The OM2 spec is still draft — some details may change, but no known blockers for the features covered here
  • See also: OM 2.0 upgrade guide for SDK maintainers

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