diff --git a/CHANGELOG.md b/CHANGELOG.md index d5f6e59c34..48b462a9b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- prometheus-exporter: fix labels out of place for data points with different + attribute sets + ([#4413](https://github.com/open-telemetry/opentelemetry-python/pull/4413)) - Tolerates exceptions when loading resource detectors via `OTEL_EXPERIMENTAL_RESOURCE_DETECTORS` ([#4373](https://github.com/open-telemetry/opentelemetry-python/pull/4373)) diff --git a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py index e0f7360e35..475cfb1266 100644 --- a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py +++ b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py @@ -224,15 +224,14 @@ def _translate_to_prometheus( metrics.append(metric) for metric in metrics: - label_valuess = [] + label_values_data_points = [] + label_keys_data_points = [] values = [] - pre_metric_family_ids = [] + per_metric_family_ids = [] metric_name = sanitize_full_name(metric.name) - metric_description = metric.description or "" - metric_unit = map_unit(metric.unit) for number_data_point in metric.data.data_points: @@ -243,7 +242,7 @@ def _translate_to_prometheus( label_keys.append(sanitize_attribute(key)) label_values.append(self._check_value(value)) - pre_metric_family_ids.append( + per_metric_family_ids.append( "|".join( [ metric_name, @@ -254,7 +253,8 @@ def _translate_to_prometheus( ) ) - label_valuess.append(label_values) + label_values_data_points.append(label_values) + label_keys_data_points.append(label_keys) if isinstance(number_data_point, HistogramDataPoint): values.append( { @@ -268,8 +268,11 @@ def _translate_to_prometheus( else: values.append(number_data_point.value) - for pre_metric_family_id, label_values, value in zip( - pre_metric_family_ids, label_valuess, values + for per_metric_family_id, label_keys, label_values, value in zip( + per_metric_family_ids, + label_keys_data_points, + label_values_data_points, + values, ): is_non_monotonic_sum = ( isinstance(metric.data, Sum) @@ -291,7 +294,7 @@ def _translate_to_prometheus( and not should_convert_sum_to_gauge ): metric_family_id = "|".join( - [pre_metric_family_id, CounterMetricFamily.__name__] + [per_metric_family_id, CounterMetricFamily.__name__] ) if metric_family_id not in metric_family_id_metric_family: @@ -311,7 +314,7 @@ def _translate_to_prometheus( or should_convert_sum_to_gauge ): metric_family_id = "|".join( - [pre_metric_family_id, GaugeMetricFamily.__name__] + [per_metric_family_id, GaugeMetricFamily.__name__] ) if ( @@ -331,7 +334,7 @@ def _translate_to_prometheus( ].add_metric(labels=label_values, value=value) elif isinstance(metric.data, Histogram): metric_family_id = "|".join( - [pre_metric_family_id, HistogramMetricFamily.__name__] + [per_metric_family_id, HistogramMetricFamily.__name__] ) if ( diff --git a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py index 623a16927b..a7a3868a8a 100644 --- a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py +++ b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py @@ -638,3 +638,59 @@ def test_semconv(self): """ ), ) + + def test_multiple_data_points_with_different_label_sets(self): + hist_point_1 = HistogramDataPoint( + attributes={"http_target": "/foobar", "net_host_port": 8080}, + start_time_unix_nano=1641946016139533244, + time_unix_nano=1641946016139533244, + count=6, + sum=579.0, + bucket_counts=[1, 3, 2], + explicit_bounds=[123.0, 456.0], + min=1, + max=457, + ) + hist_point_2 = HistogramDataPoint( + attributes={"net_host_port": 8080}, + start_time_unix_nano=1641946016139533245, + time_unix_nano=1641946016139533245, + count=7, + sum=579.0, + bucket_counts=[1, 3, 3], + explicit_bounds=[123.0, 456.0], + min=1, + max=457, + ) + + metric = Metric( + name="http.server.request.duration", + description="test multiple label sets", + unit="s", + data=Histogram( + data_points=[hist_point_1, hist_point_2], + aggregation_temporality=AggregationTemporality.CUMULATIVE, + ), + ) + + self.verify_text_format( + metric, + dedent( + """\ + # HELP http_server_request_duration_seconds test multiple label sets + # TYPE http_server_request_duration_seconds histogram + http_server_request_duration_seconds_bucket{http_target="/foobar",le="123.0",net_host_port="8080"} 1.0 + http_server_request_duration_seconds_bucket{http_target="/foobar",le="456.0",net_host_port="8080"} 4.0 + http_server_request_duration_seconds_bucket{http_target="/foobar",le="+Inf",net_host_port="8080"} 6.0 + http_server_request_duration_seconds_count{http_target="/foobar",net_host_port="8080"} 6.0 + http_server_request_duration_seconds_sum{http_target="/foobar",net_host_port="8080"} 579.0 + # HELP http_server_request_duration_seconds test multiple label sets + # TYPE http_server_request_duration_seconds histogram + http_server_request_duration_seconds_bucket{le="123.0",net_host_port="8080"} 1.0 + http_server_request_duration_seconds_bucket{le="456.0",net_host_port="8080"} 4.0 + http_server_request_duration_seconds_bucket{le="+Inf",net_host_port="8080"} 7.0 + http_server_request_duration_seconds_count{net_host_port="8080"} 7.0 + http_server_request_duration_seconds_sum{net_host_port="8080"} 579.0 + """ + ), + )