[Exporter.Prometheus] Add dots and values escaping#7439
Conversation
Add missing `in` modifiers on methods accepting parameters of type `PrometheusProtocol`.
Add support for the `dots` and `values` escaping schemes. Contributes to open-telemetry#7246.
Fix incorrectly sorted `[InlineData]` attributes.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7439 +/- ##
==========================================
+ Coverage 90.05% 90.22% +0.16%
==========================================
Files 284 279 -5
Lines 15230 15082 -148
==========================================
- Hits 13716 13607 -109
+ Misses 1514 1475 -39
Flags with carried forward coverage won't be shown. Click here to find out more.
|
- Update comments. - Add `Debug.Assert()`. - Add PR number to CHANGELOGs.
- Add missing coverage. - Remove unused code.
Add additional interesting test cases taken from https://github.com/prometheus/common/blob/43de10cc658055c6b5e4d619edc917d5af493409/model/metric_test.go.
Move entries to the right place.
There was a problem hiding this comment.
Pull request overview
Adds support in the Prometheus exporters’ text/OpenMetrics serialization pipeline for the dots and values UTF-8 name escaping schemes (as negotiated via the Accept header). This extends the existing escaping negotiation work to cover additional Prometheus-supported schemes, while also tightening some protocol- and serializer-related APIs.
Changes:
- Implement
dotsandvaluesescaping for metric and label names, including reversible escaping logic and scheme negotiation. - Update serialization to use escaping-aware metric name sets and avoid re-sanitizing already-final output keys (notably scope labels).
- Expand unit/integration/fuzz tests and snapshots to validate correct negotiation and output for the new schemes.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/snapshots/PrometheusSerializer.WriteMetric_EscapesMetricAndLabelNamesUsingNegotiatedScheme_escaping=values.verified.txt | Adds snapshot for values escaping output. |
| test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/snapshots/PrometheusSerializer.WriteMetric_EscapesMetricAndLabelNamesUsingNegotiatedScheme_escaping=underscores.verified.txt | Adds snapshot for underscores escaping output. |
| test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/snapshots/PrometheusSerializer.WriteMetric_EscapesMetricAndLabelNamesUsingNegotiatedScheme_escaping=dots.verified.txt | Adds snapshot for dots escaping output. |
| test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusSerializerTests.cs | Adds serializer test covering negotiated escaping schemes with snapshot verification. |
| test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusMetricTests.cs | Validates counter naming/suffix behavior under dots/values vs default. |
| test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusIntegrationTests.cs | Extends integration tests to include escaping=dots and escaping=values. |
| test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusEscapingTests.cs | New unit tests for escaping transformations and scheme parsing. |
| test/OpenTelemetry.Exporter.Prometheus.HttpListener.Tests/PrometheusAcceptHeaders.cs | Extends Accept-header negotiation test cases for dots/values. |
| test/OpenTelemetry.Exporter.Prometheus.HttpListener.FuzzTests/PrometheusSerializerFuzzTests.cs | Adds property tests to ensure escaping matches reference implementation and buffer overload matches string overload. |
| test/OpenTelemetry.Exporter.Prometheus.AspNetCore.Tests/PrometheusIntegrationTests.cs | Extends ASP.NET Core integration tests to include escaping=dots and escaping=values. |
| src/OpenTelemetry.Exporter.Prometheus.HttpListener/OpenTelemetry.Exporter.Prometheus.HttpListener.csproj | Links IsExternalInit shim into the project. |
| src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/Shared/Serialization/TextFormatSerializer.cs | Adds escaping awareness to serialization; updates tag-writing to use output keys and structural suffix helpers. |
| src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/Shared/Serialization/PrometheusTextSerializer.cs | Uses escaping-specific name sets for metric naming/bytes. |
| src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/Shared/Serialization/OpenMetricsSerializer.cs | Uses escaping-specific name sets for OpenMetrics naming/bytes; updates created metric naming. |
| src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/Shared/PrometheusProtocol.cs | Adds dots/values scheme constants and stores parsed EscapingScheme in the protocol. |
| src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/Shared/PrometheusMetric.cs | Refactors metric naming to compute/caches per-escaping NameSet (underscores eagerly; others lazily). |
| src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/Shared/PrometheusEscaping.cs | New escaping implementation for reversible dots and values schemes. |
| src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/Shared/PrometheusCollectionManager.cs | Adds in modifiers to reduce copying and threads escaping through response generation. |
| src/OpenTelemetry.Exporter.Prometheus.HttpListener/Internal/Shared/EscapingScheme.cs | New enum describing supported escaping schemes. |
| src/OpenTelemetry.Exporter.Prometheus.HttpListener/CHANGELOG.md | Documents added dots/values negotiation support. |
| src/OpenTelemetry.Exporter.Prometheus.AspNetCore/CHANGELOG.md | Documents added dots/values negotiation support. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Update comments.
Add missing `this`.
- Handle label vs. metric name escaping correctly. - Fix colliding exemplars with different escapings. - Fix scope labels not being escaped.
Contributes to #7246.
Changes
dotsandvaluesescaping schemes.inmodifiers on methods accepting parameters of typePrometheusProtocol.Benchmark Results
Summary (generated by Claude 🐙)
Method:
./benchmark.ps1 @("*PrometheusSerializerBenchmarks*") -EnableMemoryDiagnoser(BenchmarkDotNetDefaultjob, .NET 10.0). Ratio = target ÷ baseline (< 1.0 = improvement).Conclusion: net improvement, no regressions.
@1000cases).Root cause of the improvement: the label-writing fast path previously re-sanitized every label key — including
otel_scope_*scope labels — viaGetSanitizedLabelKey(aStringBuilder+string allocation per label). The change writes scope labels' already-formed output key directly and writes all output keys with a plain byte copy instead of re-running the normalization. Since every metric carries at leastotel_scope_name, this removes a string allocation per scope label and does less per-character work.Notes (not regressions):
WriteHistogramMetric @1 = 1.03is a single-invocation artifact (~47 ns, within ~1.5× the error bars) and is contradicted by the same path being faster at@1000(0.96) and@10000(0.94).WriteMetricWithTypedLabels @10000 = 0.71is flattered by a noisymainmeasurement (StdDev ±14%); the representative figure is the@1000case (0.92).Detailed Results
Expand to view
main
This PR
Merge requirement checklist
CHANGELOG.mdfiles updated for non-trivial changesChanges in public API reviewed (if applicable)