Skip to content

feat: compositeValues and exemplarCompliance flags for OM2 writer - #1991

Merged
zeitlinger merged 5 commits into
mainfrom
feat/om2-composite-values
Apr 23, 2026
Merged

feat: compositeValues and exemplarCompliance flags for OM2 writer#1991
zeitlinger merged 5 commits into
mainfrom
feat/om2-composite-values

Conversation

@zeitlinger

@zeitlinger zeitlinger commented Apr 9, 2026

Copy link
Copy Markdown
Member

Fixes #1989

Summary

  • compositeValues=true: Histogram, GaugeHistogram, and Summary are
    written as a single composite-value line per the OM2 spec:

    foo {count:17,sum:324789.3,bucket:[0.1:8,0.25:10,0.5:11,1.0:14,+Inf:17]} 1520879607.789 st@1520430000.123
    bar {count:17,sum:324789.3,quantile:[0.95:123.7,0.99:150.0]}
    
    • Bucket counts are cumulative (same semantics as OM1 _bucket lines)
    • GaugeHistogram uses gcount/gsum per spec (note: the tracking issue
      says "count/sum" but the spec is explicit about gcount/gsum)
    • Created timestamp moves inline as st@; no separate _created line
    • Latest exemplar (if present) is appended inline
    • No separate _bucket, _count, _sum lines emitted
    • When compositeValues=false (default), output is unchanged from before
  • exemplarCompliance=true: Exemplars without a timestamp are silently
    dropped. The OM2 spec mandates that all exemplars MUST have a timestamp.
    The 128-char LabelSet limit from OM1 was never enforced in the writer, so
    no change needed there.

Test plan

  • testCompositeHistogram — basic single-line histogram output
  • testCompositeHistogramWithLabelsTimestampAndCreated — labels, scrape
    timestamp, and st@ start timestamp
  • testCompositeHistogramWithExemplar — exemplar appended inline
  • testCompositeGaugeHistogramgcount/gsum fields
  • testCompositeSummary — basic single-line summary output
  • testCompositeSummaryWithCreatedAndExemplarst@ + exemplar
  • testExemplarComplianceSkipsExemplarWithoutTimestamp — compliance mode
    drops exemplars without timestamps; default mode still emits them
  • All 22 existing tests pass unchanged (non-composite path unaffected)
- compositeValues=true: write Histogram/GaugeHistogram/Summary as a
  single composite-value line per the OM2 spec, e.g.
    foo {count:17,sum:324789.3,bucket:[0.1:8,0.25:10,+Inf:17]} st@0.5
  GaugeHistogram uses gcount/gsum per spec. Replaces separate _bucket,
  _count, _sum, _created lines; created timestamp moves inline as st@.
  Exemplar (latest) is appended inline when present.
- exemplarCompliance=true: skip exemplars without a timestamp, as the
  OM2 spec mandates timestamps on all exemplars (MUST).

Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
@zeitlinger
zeitlinger marked this pull request as ready for review April 10, 2026 12:09
When compositeValues=false, OM2 histogram and summary output is
identical to OM1 format. Delegate to om1Writer instead of duplicating
writeClassicHistogramBuckets, writeNonCompositeSummaryDataPoint, and
writeCountAndSum.

Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
Apply the same delegation pattern as histogram/summary: when
exemplarCompliance=false (default), delegate writeScrapeTimestampAndExemplar
and writeExemplar to om1Writer. When exemplarCompliance=true, apply the
OM2 spec requirement (drop exemplars without timestamps) then delegate
the actual writing to om1Writer.writeExemplar.

Extracts writeExemplar from om1Writer.writeScrapeTimestampAndExemplar so
OM2 can delegate exemplar formatting without duplicating it.

Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds OM2 feature-flagged behavior to the OpenMetrics 2.0 text writer to support OM2 composite values for distributions and to enforce OM2 exemplar timestamp requirements when requested.

Changes:

  • Add compositeValues mode to emit Histogram/GaugeHistogram/Summary as single-line OM2 CompositeValue entries (including inline st@ start timestamps and inline exemplars).
  • Add exemplarCompliance mode to drop exemplars that lack timestamps (per OM2 MUST requirement).
  • Refactor OM1 writer helpers (histogram/summary/exemplar writing) to be reusable by the OM2 writer.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
prometheus-metrics-exposition-textformats/src/test/java/io/prometheus/metrics/expositionformats/OpenMetrics2TextFormatWriterTest.java Adds tests covering composite-value output and exemplar-compliance behavior.
prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/OpenMetricsTextFormatWriter.java Exposes histogram/summary/exemplar helper methods at package scope and factors out writeExemplar(...).
prometheus-metrics-exposition-textformats/src/main/java/io/prometheus/metrics/expositionformats/OpenMetrics2TextFormatWriter.java Implements composite-value rendering and exemplar compliance; delegates non-composite histogram/summary rendering to OM1 writer.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
@zeitlinger
zeitlinger merged commit ff48ae8 into main Apr 23, 2026
13 checks passed
@zeitlinger
zeitlinger deleted the feat/om2-composite-values branch April 23, 2026 05:54
zeitlinger pushed a commit that referenced this pull request Apr 27, 2026
🤖 I have created a release *beep* *boop*
---


##
[1.6.0](v1.5.1...v1.6.0)
(2026-04-25)


### Features

* Add logic for handling content negotation flag with OM2
([#1986](#1986))
([0fa1ad7](0fa1ad7))
* Add OM2 native histogram text output
([#2042](#2042))
([dec8e5b](dec8e5b))
* add OTel preserve_names for scrape-time suffix handling
([#1956](#1956))
([f794288](f794288))
* compositeValues and exemplarCompliance flags for OM2 writer
([#1991](#1991))
([ff48ae8](ff48ae8))
* move suffix handling to scrape time
([#1955](#1955))
([5a5106c](5a5106c))
* OM2 writer outputs names as provided, no suffix appending
([#1957](#1957))
([5ce2b57](5ce2b57))


### Bug Fixes

* **deps:** update dependency com.google.guava:guava to v33.6.0-jre
([#2021](#2021))
([1382693](1382693))
* **deps:** update dependency commons-io:commons-io to v2.22.0
([#2044](#2044))
([9e05c1d](9e05c1d))
* **deps:** update dependency
io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom-alpha
to v2.27.0-alpha
([#2022](#2022))
([30ac534](30ac534))
* **deps:** update dependency
io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom-alpha
to v2.27.0-alpha
([#2023](#2023))
([2d51a32](2d51a32))
* **deps:** update dependency io.prometheus:prometheus-metrics-bom to
v1.5.1 ([#2004](#2004))
([650ce4b](650ce4b))
* **deps:** update dependency org.apache.tomcat.embed:tomcat-embed-core
to v11.0.21
([#2005](#2005))
([7a36df7](7a36df7))
* **deps:** update dependency
org.springframework.boot:spring-boot-starter-parent to v4.0.5
([#2006](#2006))
([0106c18](0106c18))
* **deps:** update dependency
org.springframework.boot:spring-boot-starter-parent to v4.0.6
([#2046](#2046))
([40a9db8](40a9db8))
* **deps:** update jetty monorepo to v12.1.8
([#2007](#2007))
([acab5b2](acab5b2))
* **deps:** update protobuf
([#2024](#2024))
([8e2214e](8e2214e))
* pass release tag as input to deploy workflow
([#1982](#1982))
([165c921](165c921))
* pin grafana/otel-lgtm to 0.7.2 in OATs acceptance test
([#1992](#1992))
([f17ad9a](f17ad9a))
* Relax metric name validation in Dropwizard5
([#1985](#1985))
([deb782f](deb782f))
* stabilize flaky timer and thread count tests
([#1973](#1973))
([ce5867b](ce5867b))
* Tighten OM2 summary and start timestamp output
([#2041](#2041))
([5699469](5699469))
* trigger Maven deploy from release-please via workflow_dispatch
([#1981](#1981))
([698f956](698f956))

---
> [!IMPORTANT]
> Close and reopen this PR to trigger CI checks.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants