Skip to content

feat: move suffix handling to scrape time - #1955

Merged
zeitlinger merged 2 commits into
mainfrom
scrape-time-suffix-core
Mar 20, 2026
Merged

feat: move suffix handling to scrape time#1955
zeitlinger merged 2 commits into
mainfrom
scrape-time-suffix-core

Conversation

@zeitlinger

@zeitlinger zeitlinger commented Mar 17, 2026

Copy link
Copy Markdown
Member

Summary

Moves metric name suffix handling (_total, _info, unit suffixes)
from creation time to scrape time. Closes #1941, part of #1942.

  • OM1: smart-appends suffixes (skips if already present)
  • Registry: detects cross-format name collisions at registration
    time

Key changes

  • Remove all reserved metric name suffixes from PrometheusNaming
  • Store original user-provided name separately from exposition base
    name in MetricMetadata (originalName vs expositionBaseName)
  • Smart-append logic in OM1/protobuf writers for _total and _info
  • Two-layer collision detection in PrometheusRegistry
    (base name + exposition names)

Key table

User provides OM1
Counter("events") events_total
Counter("events_total") events_total
Counter("req").unit(BYTES) req_bytes_total
Counter("req_bytes").unit(BYTES) req_bytes_total
Gauge("events_total") events_total

PR stack

  1. This PR — core model + OM1/protobuf writers
  2. OTel preserve_names (stacked on this)
  3. OM2 writer no-suffix (stacked on this)

Test plan

  • mise run compile passes
  • New tests for MetricMetadata 5-arg constructor and
    field accessors
  • New PrometheusRegistryTest covers collision detection
  • Existing snapshot tests updated

Part of #1912.

@jaydeluca

jaydeluca commented Mar 17, 2026

Copy link
Copy Markdown
Member

i accidentally commented on this one because it has a lot of the same changes as #1956 , is this separate?

edit: just noticed the "blocked by" on the other PR, so I guess i did them out of order

Move metric name suffix handling (_total, _info, unit suffixes) from
creation time to scrape time. Each format writer now owns its suffix
conventions:

- OM1: smart-appends suffixes (skip if already present)
- Registry detects cross-format name collisions at registration time

Key changes:
- Remove all reserved metric name suffixes from PrometheusNaming
- Store original user-provided name separately from exposition base
  name in MetricMetadata (originalName vs expositionBaseName)
- Smart-append logic in OM1/protobuf writers for _total and _info
- Two-layer collision detection in PrometheusRegistry

Closes #1941
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
@zeitlinger
zeitlinger force-pushed the scrape-time-suffix-core branch from e63ef43 to 0787661 Compare March 18, 2026 08:03
@zeitlinger

Copy link
Copy Markdown
Member Author

i accidentally commented on this one because it has a lot of the same changes as #1956 , is this separate?

edit: just noticed the "blocked by" on the other PR, so I guess i did them out of order

no worries - I'll apply the feedback to the correct pr

- Fix race condition: use putIfAbsent for atomic exposition name claiming
  with proper rollback on failure
- Extract claimExpositionNames/releaseExpositionNames for single rollback path
- Rename Info test methods to reflect that _info suffix is now allowed
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
@zeitlinger

Copy link
Copy Markdown
Member Author

i accidentally commented on this one because it has a lot of the same changes as #1956 , is this separate?
edit: just noticed the "blocked by" on the other PR, so I guess i did them out of order

no worries - I'll apply the feedback to the correct pr

did that now

@zeitlinger
zeitlinger merged commit 5a5106c into main Mar 20, 2026
16 of 17 checks passed
@zeitlinger
zeitlinger deleted the scrape-time-suffix-core branch March 20, 2026 09:36
zeitlinger added a commit that referenced this pull request Mar 20, 2026
## Summary

Adds `preserve_names` configuration to the OpenTelemetry exporter.
When enabled, metric names are passed through exactly as the user
wrote them instead of stripping `_total` and unit suffixes.
Part of #1942.

### Key changes

- Add `preserve_names` to `ExporterOpenTelemetryProperties`
- `MetricDataFactory` uses `originalName` + `preserve_names` to
  decide naming
- `OtelAutoConfig` wires the new property

### Key table

| User provides | OTel | OTel preserve_names |
|---|---|---|
| `Counter("events")` | `events` | `events` |
| `Counter("events_total")` | `events` | `events_total` |
| `Counter("req").unit(BYTES)` | name `req`, unit `By` | name `req`,
unit `By` |
| `Counter("req_bytes").unit(BYTES)` | name `req`, unit `By` | name
`req_bytes`, unit `By` |
| `Gauge("events_total")` | `events_total` | `events_total` |

### PR stack

1. Core model + OM1/protobuf writers (#1955)
2. **This PR** — OTel `preserve_names`
3. OM2 writer no-suffix (independent)

## Test plan

- [x] `mise run compile` passes
- [x] Tests for `preserve_names=true` with units, unit already in
  name, and without unit
- [x] `OtelAutoConfigTest` covers new property wiring

Part of #1912.

---------

Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
zeitlinger added a commit that referenced this pull request Mar 20, 2026
## Summary

The OM2 writer now uses `expositionBaseName` directly instead of
appending `_total` (counters) or unit suffixes. The `_info` suffix
is enforced per the OM2 spec (MUST). Part of #1942.

### Key table

| User provides | OM1 | OM2 |
|---|---|---|
| `Counter("events")` | `events_total` | `events` |
| `Counter("events_total")` | `events_total` | `events_total` |
| `Counter("req").unit(BYTES)` | `req_bytes_total` | `req_bytes` |
| `Counter("req_bytes").unit(BYTES)` | `req_bytes_total` | `req_bytes` |
| `Gauge("events_total")` | `events_total` | `events_total` |
| `Info("target")` | `target_info` | `target_info` |

### PR stack

1. Core model + OM1/protobuf writers (#1955)
2. OTel `preserve_names` (independent)
3. **This PR** — OM2 writer no-suffix

## Test plan

- [x] `mise run compile` passes
- [x] OM2-specific tests verify no `_total`/unit suffix appending
- [x] `_info` suffix enforcement tested

Part of #1912.

---------

Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
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>
zeitlinger added a commit that referenced this pull request May 13, 2026
Fixes #2095

## Summary

Restores OM1/protobuf compatibility for dotted gauge names after
`feat: move suffix handling to scrape time (#1955)`.

The bug was that non-OpenMetrics exposition changed visible output for
gauge
names that merely ended in suffix-like dotted strings such as `.created`
and
`.total`.

Examples:

- `Gauge("test3.created")` regressed from `test3` to `test3_created`
- `Gauge("test6.total")` regressed from `test6` to `test6_total`

This PR restores the legacy OM1/protobuf behavior while keeping
OpenMetrics on
literal-name handling.

This is the extracted prom-side fix from #2093. The Micrometer workflow
and
related downstream testing were split into a stacked follow-up PR so
this can
merge independently.

## What changed

- Fix OM1 text exposition for dotted gauge names ending in `.created`
and
  `.total`
- Fix protobuf exposition for the same compatibility cases
- Add regression tests that cover the restored OM1/protobuf behavior and
the
  preserved OpenMetrics behavior
- Clean up protobuf family-name resolution so legacy gauge handling
lives in
  one path instead of pre-rewriting metadata objects

## Follow-up stacked PR

- Micrometer workflow/task split:
zeitlinger#1

## Related

- Replaces: #2093
- Issue: #2095

## Testing

- `mise run build`
- `mise run lint`
- `./mvnw test -pl
prometheus-metrics-exposition-textformats,prometheus-metrics-exposition-formats
-Dtest=ExpositionFormatsTest,ProtobufExpositionFormatsTest,DuplicateNamesProtobufTest
-Dcoverage.skip=true -Dcheckstyle.skip=true`
jaydeluca pushed a commit that referenced this pull request Jun 4, 2026
Fixes #2095

## Summary

Restores OM1/protobuf compatibility for dotted gauge names after
`feat: move suffix handling to scrape time (#1955)`.

The bug was that non-OpenMetrics exposition changed visible output for
gauge
names that merely ended in suffix-like dotted strings such as `.created`
and
`.total`.

Examples:

- `Gauge("test3.created")` regressed from `test3` to `test3_created`
- `Gauge("test6.total")` regressed from `test6` to `test6_total`

This PR restores the legacy OM1/protobuf behavior while keeping
OpenMetrics on
literal-name handling.

This is the extracted prom-side fix from #2093. The Micrometer workflow
and
related downstream testing were split into a stacked follow-up PR so
this can
merge independently.

## What changed

- Fix OM1 text exposition for dotted gauge names ending in `.created`
and
  `.total`
- Fix protobuf exposition for the same compatibility cases
- Add regression tests that cover the restored OM1/protobuf behavior and
the
  preserved OpenMetrics behavior
- Clean up protobuf family-name resolution so legacy gauge handling
lives in
  one path instead of pre-rewriting metadata objects

## Follow-up stacked PR

- Micrometer workflow/task split:
zeitlinger#1

## Related

- Replaces: #2093
- Issue: #2095

## Testing

- `mise run build`
- `mise run lint`
- `./mvnw test -pl
prometheus-metrics-exposition-textformats,prometheus-metrics-exposition-formats
-Dtest=ExpositionFormatsTest,ProtobufExpositionFormatsTest,DuplicateNamesProtobufTest
-Dcoverage.skip=true -Dcheckstyle.skip=true`

Signed-off-by: Jay DeLuca <jaydeluca4@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants