You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Impl notes: exemplars use hex trace_id/span_id, first exemplar per data point (histogram → +Inf bucket); _total suppression rewrites family.samples; conflicting-type tracking resets per collect().
📣 Public-facing draft — to be opened upstream in open-telemetry/opentelemetry-python. No internal references; copy verbatim.
Title: Prometheus exporter diverges from the Prometheus/OpenMetrics compatibility spec (attribute collisions, exemplars, counter suffix, conflicting types, reserved scope labels)
The Python Prometheus exporter (exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py) is missing several behaviors required by the Prometheus and OpenMetrics compatibility specification (specification/compatibility/prometheus_and_openmetrics.md) that Go and Java already provide.
Colliding sanitized attribute keys silently overwrite each other. In _collect_data_points, attributes are sanitized and written into a dict keyed by the sanitized label: labels[label] = self._check_value(value). Two source keys that sanitize to the same label (e.g. foo.bar and foo/bar → foo_bar) collide and the last wins. Fix: accumulate all values per sanitized label and ;-join them, sorted by original key.
Exemplars are never emitted._populate_counter_family / _populate_histogram_family call add_metric(...) without reading data_point.exemplars, so SDK-recorded exemplars are lost. Fix: map OTel exemplars to prometheus_client exemplars (trace_id/span_id labels), attaching on counters and (for histograms) the +Inf bucket.
The _total suffix cannot be disabled.CounterMetricFamily always appends _total with no opt-out. Fix: add a without_counter_suffixes constructor flag on PrometheusMetricReader.
No conflicting-TYPE handling._translate_metric unconditionally builds a family per metric; two metrics sharing a name but different Prometheus types produce an exposition Prometheus rejects. Fix: track the type chosen per metric name within a collection, drop later conflicting families, and warn.
Scope attributes can clobber reserved otel_scope_* labels. A data-point attribute that sanitizes to otel_scope_name/_version/_schema_url overwrites the scope-provided value. Fix: skip data-point attributes whose sanitized name collides with a reserved otel_scope_* label.
Activity
ocelotl commentedon Jul 22, 2026
🔒 Internal (dash0) — not for upstream.
oss_18_prometheus_openmetrics_compat388bd1b3a· PR: oss_18_prometheus_openmetrics_compat #35_totalsuppression rewrites family.samples; conflicting-type tracking resets per collect().ocelotl commentedon Jul 22, 2026
📣 Public-facing draft — to be opened upstream in
open-telemetry/opentelemetry-python. No internal references; copy verbatim.Title: Prometheus exporter diverges from the Prometheus/OpenMetrics compatibility spec (attribute collisions, exemplars, counter suffix, conflicting types, reserved scope labels)
The Python Prometheus exporter (
exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py) is missing several behaviors required by the Prometheus and OpenMetrics compatibility specification (specification/compatibility/prometheus_and_openmetrics.md) that Go and Java already provide.Colliding sanitized attribute keys silently overwrite each other. In
_collect_data_points, attributes are sanitized and written into a dict keyed by the sanitized label:labels[label] = self._check_value(value). Two source keys that sanitize to the same label (e.g.foo.barandfoo/bar→foo_bar) collide and the last wins. Fix: accumulate all values per sanitized label and;-join them, sorted by original key.Exemplars are never emitted.
_populate_counter_family/_populate_histogram_familycalladd_metric(...)without readingdata_point.exemplars, so SDK-recorded exemplars are lost. Fix: map OTel exemplars toprometheus_clientexemplars (trace_id/span_idlabels), attaching on counters and (for histograms) the+Infbucket.The
_totalsuffix cannot be disabled.CounterMetricFamilyalways appends_totalwith no opt-out. Fix: add awithout_counter_suffixesconstructor flag onPrometheusMetricReader.No conflicting-TYPE handling.
_translate_metricunconditionally builds a family per metric; two metrics sharing a name but different Prometheus types produce an exposition Prometheus rejects. Fix: track the type chosen per metric name within a collection, drop later conflicting families, and warn.Scope attributes can clobber reserved
otel_scope_*labels. A data-point attribute that sanitizes tootel_scope_name/_version/_schema_urloverwrites the scope-provided value. Fix: skip data-point attributes whose sanitized name collides with a reservedotel_scope_*label.