Skip to content

oss_18_prometheus_openmetrics_compat #29

Open

Description

@ocelotl

Prometheus/OpenMetrics exporter compatibility bundle: merge colliding sanitized attribute keys, emit exemplars, allow disabling total suffix, dedup/drop conflicting-type metadata, and protect reserved otel_scope* labels (Linear OSS-18, findings M5-M9).

Branch / worktree: oss_18_prometheus_openmetrics_compat

Linear issue: https://linear.app/dash0/issue/OSS-18/m5-m9-prometheusopenmetrics-compatibility-bundle

Activity

  1. ocelotl commented on Jul 22, 2026

    @ocelotl
    OwnerAuthor

    🔒 Internal (dash0) — not for upstream.

  2. ocelotl commented on Jul 22, 2026

    @ocelotl
    OwnerAuthor

    📣 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.

    1. 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.

    2. 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.

    3. The _total suffix cannot be disabled. CounterMetricFamily always appends _total with no opt-out. Fix: add a without_counter_suffixes constructor flag on PrometheusMetricReader.

    4. 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.

    5. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions