Skip to content

[Exporter.Prometheus] Update Accept/Content-Type handling to consider escaping#7209

Merged
martincostello merged 10 commits into
open-telemetry:mainfrom
martincostello:prometheus-content-type-escaping
May 5, 2026
Merged

[Exporter.Prometheus] Update Accept/Content-Type handling to consider escaping#7209
martincostello merged 10 commits into
open-telemetry:mainfrom
martincostello:prometheus-content-type-escaping

Conversation

@martincostello

Copy link
Copy Markdown
Member

Builds on top of #7208.

Changes

  • Add missing SHOULD requirement to specify the escaping value for 1.0.0 protocols.

    For PrometheusText1.0.0 and OpenMetricsText1.0.0 protocols, the Accept header SHOULD include an escaping scheme parameter: escaping=<scheme>

  • Update PrometheusSerializer to be compliant with escaping=underscores when using OpenMetrics.

Merge requirement checklist

  • CONTRIBUTING guidelines followed (license requirements, nullable enabled, static analysis, etc.)
  • Unit tests added/updated
  • Appropriate CHANGELOG.md files updated for non-trivial changes
  • Changes in public API reviewed (if applicable)

@github-actions github-actions Bot added pkg:OpenTelemetry.Exporter.Prometheus.AspNetCore Issues related to OpenTelemetry.Exporter.Prometheus.AspNetCore NuGet package pkg:OpenTelemetry.Exporter.Prometheus.HttpListener Issues related to OpenTelemetry.Exporter.Prometheus.HttpListener NuGet package labels Apr 29, 2026
@codecov

codecov Bot commented Apr 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.87%. Comparing base (0189770) to head (0115430).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #7209      +/-   ##
==========================================
+ Coverage   89.86%   89.87%   +0.01%     
==========================================
  Files         272      272              
  Lines       13489    13533      +44     
==========================================
+ Hits        12122    12163      +41     
- Misses       1367     1370       +3     
Flag Coverage Δ
unittests-Project-Experimental 89.73% <100.00%> (-0.09%) ⬇️
unittests-Project-Stable 89.63% <100.00%> (-0.15%) ⬇️
unittests-UnstableCoreLibraries-Experimental 45.29% <100.00%> (+0.26%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...metheus.AspNetCore/PrometheusExporterMiddleware.cs 98.36% <100.00%> (+0.11%) ⬆️
...tpListener/Internal/PrometheusCollectionManager.cs 87.58% <100.00%> (ø)
...s.HttpListener/Internal/PrometheusHeadersParser.cs 98.33% <100.00%> (+0.18%) ⬆️
...ometheus.HttpListener/Internal/PrometheusMetric.cs 99.43% <100.00%> (+0.06%) ⬆️
...heus.HttpListener/Internal/PrometheusSerializer.cs 98.73% <100.00%> (+0.08%) ⬆️
...s.HttpListener/Internal/PrometheusSerializerExt.cs 98.82% <100.00%> (ø)
....Prometheus.HttpListener/PrometheusHttpListener.cs 88.11% <100.00%> (ø)

... and 3 files with indirect coverage changes

@martincostello martincostello force-pushed the prometheus-content-type-escaping branch from 40e1403 to 1dd4291 Compare April 29, 2026 12:17
Comment thread src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md Outdated
Comment thread src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md Outdated
- Add missing SHOULD requirement to specify the `escaping` value for 1.0.0 protocols.
- Update `PrometheusSerializer` to be compliant with `escaping=underscores` when using OpenMetrics.
@martincostello martincostello force-pushed the prometheus-content-type-escaping branch from c3ebfae to 0ba8e0d Compare April 30, 2026 14:43
@martincostello martincostello marked this pull request as ready for review April 30, 2026 14:44
@martincostello martincostello requested a review from a team as a code owner April 30, 2026 14:44
Copilot AI review requested due to automatic review settings April 30, 2026 14:44

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

Updates the Prometheus exporters’ content negotiation and serialization to align with Prometheus/OpenMetrics “escaping” requirements, particularly escaping=underscores for OpenMetrics 1.0.0.

Changes:

  • Emit escaping=underscores in OpenMetrics Content-Type responses and require escaping=underscores when an escaping parameter is explicitly provided in Accept.
  • Update serializer label/metric name handling to better match underscore-based escaping rules (including collapsing runs of invalid characters).
  • Expand unit + fuzz + integration test coverage for the new behavior and update changelogs.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs Adds coverage for label-key normalization differences between Prometheus vs OpenMetrics paths.
test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusMetricTests.cs Adds OpenMetrics metric-name escaping/unit normalization test cases (leading digits, collapsing, unsupported chars).
test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHttpListenerTests.cs Updates expected OpenMetrics Content-Type to include escaping=underscores.
test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusHeadersParserTests.cs Adds Accept header parsing test cases for escaping=underscores and rejects unsupported schemes.
test/OpenTelemetry.Exporter.Prometheus.HttpListener.FuzzTests/PrometheusSerializerFuzzTests.cs Splits fuzz coverage for Prometheus vs OpenMetrics label-key writers and updates reference implementations.
test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusExporterMiddlewareTests.cs Updates Accept negotiation and Content-Type expectations for escaping parameter handling.
src/OpenTelemetry.Exporter.Prometheus.HttpListener/PrometheusHttpListener.cs Emits OpenMetrics Content-Type with escaping=underscores.
src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializerExt.cs Threads openMetricsRequested through tag writing so label-key escaping matches negotiated format.
src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusSerializer.cs Adds OpenMetrics-aware label-key writing and passes openMetricsRequested through label/tag/scope/target-info writers.
src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusMetric.cs Introduces EscapeOpenMetricsName and adjusts OpenMetrics naming/unit suffix logic for underscore escaping.
src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusHeadersParser.cs Extends HttpListener-side Accept parsing to validate escaping=underscores when present.
src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/PrometheusCollectionManager.cs Updates internal calls to serializer APIs that now require openMetricsRequested.
src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md Documents escaping/serialization behavior changes.
src/OpenTelemetry.Exporter.Prometheus.AspNetCore/PrometheusExporterMiddleware.cs Emits OpenMetrics Content-Type with escaping=underscores and validates escaping parameter in Accept.
src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md Documents escaping/serialization behavior changes.

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

Fix missing prefixing for metrics that start with a digit.
martincostello and others added 2 commits May 5, 2026 10:14
- Fix escaping behaviour for unit suffixes.
- Fix incorrect label escaping for `:`.
@martincostello

Copy link
Copy Markdown
Member Author

Need to patch the fuzz tests.

Update generator after changes in 2a588a0.
intellitect-bot pushed a commit to IntelliTect/EssentialCSharp.Web that referenced this pull request Jun 12, 2026
….0 (#1201)

Updated
[OpenTelemetry.Exporter.OpenTelemetryProtocol](https://github.com/open-telemetry/opentelemetry-dotnet)
from 1.15.3 to 1.16.0.

<details>
<summary>Release notes</summary>

_Sourced from [OpenTelemetry.Exporter.OpenTelemetryProtocol's
releases](https://github.com/open-telemetry/opentelemetry-dotnet/releases)._

## 1.16.0

For highlights and announcements pertaining to this release see:
[Release Notes >
1.16.0](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/RELEASENOTES.md#​1160).

The following changes are from the previous release
[1.16.0-rc.1](https://github.com/open-telemetry/opentelemetry-dotnet/releases/tag/core-1.16.0-rc.1).

* NuGet: [OpenTelemetry
v1.16.0](https://www.nuget.org/packages/OpenTelemetry/1.16.0)

  No notable changes.

See
[CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.16.0/src/OpenTelemetry/CHANGELOG.md)
for details.

* NuGet: [OpenTelemetry.Api
v1.16.0](https://www.nuget.org/packages/OpenTelemetry.Api/1.16.0)

  No notable changes.

See
[CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.16.0/src/OpenTelemetry.Api/CHANGELOG.md)
for details.

* NuGet: [OpenTelemetry.Api.ProviderBuilderExtensions
v1.16.0](https://www.nuget.org/packages/OpenTelemetry.Api.ProviderBuilderExtensions/1.16.0)

  No notable changes.

See
[CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.16.0/src/OpenTelemetry.Api.ProviderBuilderExtensions/CHANGELOG.md)
for details.

* NuGet: [OpenTelemetry.Exporter.Console
v1.16.0](https://www.nuget.org/packages/OpenTelemetry.Exporter.Console/1.16.0)

  No notable changes.

See
[CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.16.0/src/OpenTelemetry.Exporter.Console/CHANGELOG.md)
for details.

* NuGet: [OpenTelemetry.Exporter.InMemory
v1.16.0](https://www.nuget.org/packages/OpenTelemetry.Exporter.InMemory/1.16.0)

  No notable changes.

See
[CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.16.0/src/OpenTelemetry.Exporter.InMemory/CHANGELOG.md)
for details.

* NuGet: [OpenTelemetry.Exporter.OpenTelemetryProtocol
v1.16.0](https://www.nuget.org/packages/OpenTelemetry.Exporter.OpenTelemetryProtocol/1.16.0)

  No notable changes.

See
[CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.16.0/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md)
for details.

* NuGet: [OpenTelemetry.Exporter.Zipkin
v1.16.0](https://www.nuget.org/packages/OpenTelemetry.Exporter.Zipkin/1.16.0)

  No notable changes.

See
[CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.16.0/src/OpenTelemetry.Exporter.Zipkin/CHANGELOG.md)
for details.

* NuGet: [OpenTelemetry.Extensions.Hosting
v1.16.0](https://www.nuget.org/packages/OpenTelemetry.Extensions.Hosting/1.16.0)

  No notable changes.

 ... (truncated)

## 1.16.0-rc.1

The following changes are from the previous release
[1.15.3](https://github.com/open-telemetry/opentelemetry-dotnet/releases/tag/core-1.15.3).

* NuGet: [OpenTelemetry
v1.16.0-rc.1](https://www.nuget.org/packages/OpenTelemetry/1.16.0-rc.1)

* Stop validating View-provided metric stream `Name` against the
instrument
    name syntax, per
[spec
clarification](open-telemetry/opentelemetry-specification#5094).

([#​7300](open-telemetry/opentelemetry-dotnet#7300))
  
* Fix incorrect validation of `OTEL_BSP_*` and `OTEL_BLRP_*` environment
    variables.

([#​7187](open-telemetry/opentelemetry-dotnet#7187))
  
* Fix observable instrument callbacks running once per reader instead of
    once per collection cycle.

([#​7188](open-telemetry/opentelemetry-dotnet#7188))
  
* Added exception safety for user-supplied `ExemplarReservoir`
implementations.
Exceptions thrown from `Offer` are now caught and logged rather than
propagating
    out of `Counter.Add`/`Histogram.Record`.

([#​7277](open-telemetry/opentelemetry-dotnet#7277))
  
* Update `OpenTelemetrySdkEventSource` to support the W3C randomness
flag.

([#​7301](open-telemetry/opentelemetry-dotnet#7301))
  
  * Added `ObservedTimestamp` property to `LogRecord`.

([#​6979](open-telemetry/opentelemetry-dotnet#6979))
  
* **Breaking Change** Explicit histogram boundaries no longer allow more
than
    10 million values.

([#​7165](open-telemetry/opentelemetry-dotnet#7165))
  
* Fixed a circular reference which could cause a `LoggerProvider` to
fail to
resolve when one of its dependencies depends on `ILogger` or
`ILoggerFactory`.
As part of this fix the `LoggerProvider` resolved from dependency
injection
is now created lazily when the first logger is created rather than when
`ILoggerProvider` or `ILoggerFactory` is resolved. A consequence is that
any
invalid configuration now surfaces when the first log record is written
instead
    of when the logging services are resolved.

([#​7308](open-telemetry/opentelemetry-dotnet#7308))

See
[CHANGELOG](https://github.com/open-telemetry/opentelemetry-dotnet/blob/core-1.16.0-rc.1/src/OpenTelemetry/CHANGELOG.md)
for details.

* NuGet: [OpenTelemetry.Api
v1.16.0-rc.1](https://www.nuget.org/packages/OpenTelemetry.Api/1.16.0-rc.1)

  * **Experimental (pre-release builds only):**
Add support for using environment variables as context propagation
carriers.

([#​7174](open-telemetry/opentelemetry-dotnet#7174))
  
* Fix `BaggagePropagator` to correctly follow Key and Value Encoding
rules as per
 ... (truncated)

## 1.16.0-beta.1

The following changes are from the previous release
[1.15.3-beta.1](https://github.com/open-telemetry/opentelemetry-dotnet/releases/tag/coreunstable-1.15.3-beta.1).

* NuGet: [OpenTelemetry.Exporter.Prometheus.AspNetCore
v1.16.0-beta.1](https://www.nuget.org/packages/OpenTelemetry.Exporter.Prometheus.AspNetCore/1.16.0-beta.1)

* Fixed scrape response cache freshness using monotonic time so it is
not
    affected by NTP system clock adjustments.

([#​7253](open-telemetry/opentelemetry-dotnet#7253))
  
  * **Breaking Change** Removed `DisableTimestamp` property from
    `PrometheusAspNetCoreOptions`.

([#​7176](open-telemetry/opentelemetry-dotnet#7176))
  
* Fixed the serialization of `NaN`, `PositiveInfinity`, and
`NegativeInfinity`
    values in Prometheus metrics to be compliant with the specification.

([#​7179](open-telemetry/opentelemetry-dotnet#7179))
  
* Fixed loss of precision when serializing `double` and `float` values
in
Prometheus metrics to be compliant with the specification by using 17
    significant digits to represent such values.

([#​7179](open-telemetry/opentelemetry-dotnet#7179))
  
* Fix non-ASCII characters in metric names and unit strings not being
sanitized
    correctly during Prometheus serialization.

([#​7184](open-telemetry/opentelemetry-dotnet#7184))
  
* Fix case where reader tracking could be reset while readers were still
active.

([#​7190](open-telemetry/opentelemetry-dotnet#7190))
  
* Improve `Accept` header handling for format negotiation so OpenMetrics
is
    selected correctly by considering whitespace and `q` weights.

([#​7208](open-telemetry/opentelemetry-dotnet#7208))
  
  * Emit OpenMetrics exemplars for counters and histogram buckets.

([#​7222](open-telemetry/opentelemetry-dotnet#7222))
  
* Fix incorrect handling of untyped metrics when using OpenMetrics
format.

([#​7219](open-telemetry/opentelemetry-dotnet#7219))
  
* Fix Prometheus/OpenMetrics serialization to emit metric and label
names
containing `_` instead of dropping them and prefixing leading digits.
    Invalid characters are replaced with `_` instead of being dropped.

([#​7209](open-telemetry/opentelemetry-dotnet#7209))
  
* Add `escaping=underscores` to the `Accept` header handling for content
    negotiation so OpenMetrics are handled correctly.

([#​7209](open-telemetry/opentelemetry-dotnet#7209))
  
* Omit histogram `_sum` and `_count` in OpenMetrics when negative bucket
    thresholds are present.

([#​7221](open-telemetry/opentelemetry-dotnet#7221))
 ... (truncated)

Commits viewable in [compare
view](open-telemetry/opentelemetry-dotnet@core-1.15.3...core-1.16.0).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=OpenTelemetry.Exporter.OpenTelemetryProtocol&package-manager=nuget&previous-version=1.15.3&new-version=1.16.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

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

Labels

pkg:OpenTelemetry.Exporter.Prometheus.AspNetCore Issues related to OpenTelemetry.Exporter.Prometheus.AspNetCore NuGet package pkg:OpenTelemetry.Exporter.Prometheus.HttpListener Issues related to OpenTelemetry.Exporter.Prometheus.HttpListener NuGet package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants