Skip to content

Prometheus exporter puts label values under the wrong label names when two instruments share a name #5685

Description

@jbyers44

Describe your environment

OS: macOS
Python version: 3.10.18
Package version: opentelemetry-sdk 1.44.0, opentelemetry-exporter-prometheus 0.65b0.

The issue also reproduces on main as of today.

What happened?

The exporter puts label values under the wrong names when two instruments from different meters share a name, unit, and description but not attribute keys. The family id is name|description|unit, so both go into one family. _get_or_create_family returns the existing family, which has the first metric's label names. The second metric's values go into those names by position.

This regression started with #4869, which removed label keys from the family id to fix duplicate HELP/TYPE lines (#4868). 1.39.1 / 0.60b1 exports both series correctly. 1.40.0 / 0.61b0 mislabels the second one.

We hit this bug with opentelemetry-instrumentation-httpx and opentelemetry-instrumentation-requests. Both emit http.client.duration with the same unit and description and different keys. The result is series like http_method="200".

Steps to Reproduce

from opentelemetry.exporter.prometheus import PrometheusMetricReader
from opentelemetry.sdk.metrics import MeterProvider
from prometheus_client import REGISTRY, generate_latest

provider = MeterProvider(metric_readers=[PrometheusMetricReader()])

first = provider.get_meter("scope.first").create_histogram("request.duration", unit="ms", description="Duration.")
second = provider.get_meter("scope.second").create_histogram("request.duration", unit="ms", description="Duration.")

first.record(1, {"method": "GET", "status": "200"})
second.record(1, {"host": "example.com", "method": "POST", "status": "500"})

for line in generate_latest(REGISTRY).decode().splitlines():
    if line.startswith("request_duration_milliseconds_count"):
        print(line)

Expected Result

Each value under its own label, with one HELP and one TYPE line:

request_duration_milliseconds_count{host="",method="GET",otel_scope_name="scope.first",otel_scope_schema_url="",otel_scope_version="",status="200"} 1.0
request_duration_milliseconds_count{host="example.com",method="POST",otel_scope_name="scope.second",otel_scope_schema_url="",otel_scope_version="",status="500"} 1.0

Actual Result

request_duration_milliseconds_count{method="GET",otel_scope_name="scope.first",otel_scope_schema_url="",otel_scope_version="",status="200"} 1.0
request_duration_milliseconds_count{method="example.com",otel_scope_name="POST",otel_scope_schema_url="scope.second",otel_scope_version="",status=""} 1.0

The series has no host label, method gets the host, otel_scope_name gets the method, and status is empty. If second records first, the second series is correct and the exporter mislabels the first series.

Additional context

#4413 fixed the same kind of bug for points with different attribute sets within one instrument. Its test, test_multiple_data_points_with_different_label_sets, still passes, since every point there comes from one metric.

Would you like to implement a fix?

Yes

Tip

React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.

Activity

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

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions