From c6c9fb6fcf52a293208fb0bb2818ac199f123ede Mon Sep 17 00:00:00 2001 From: manvi Date: Fri, 17 Apr 2026 08:39:26 +0530 Subject: [PATCH 1/6] feat(exporter/prometheus): add default_aggregation parameter to PrometheusMetricReader --- CHANGELOG.md | 2 ++ .../exporter/prometheus/__init__.py | 11 +++++++++-- .../tests/test_prometheus_exporter.py | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afec89b73f..f456799ee1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- `opentelemetry-exporter-prometheus`: Add `default_aggregation` parameter to `PrometheusMetricReader` to allow configuring default aggregation per instrument kind + ([#5109](https://github.com/open-telemetry/opentelemetry-python/issues/5109)) - `opentelemetry-sdk`: fix YAML structure injection via environment variable substitution in declarative file configuration; values containing newlines are now emitted as quoted YAML scalars per spec requirement ([#5091](https://github.com/open-telemetry/opentelemetry-python/pull/5091)) - `opentelemetry-sdk`: Add `create_logger_provider`/`configure_logger_provider` to declarative file configuration, enabling LoggerProvider instantiation from config files without reading env vars 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 608d8f6d30..6525cecdc2 100644 --- a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py +++ b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py @@ -67,7 +67,7 @@ from json import dumps from logging import getLogger from os import environ -from typing import Deque, Dict, Iterable, Sequence, Tuple, Union +from typing import Deque, Dict, Iterable, Optional, Sequence, Tuple, Union from prometheus_client import start_http_server from prometheus_client.core import ( @@ -105,6 +105,9 @@ MetricsData, Sum, ) +from opentelemetry.sdk.metrics.view import ( + Aggregation, +) from opentelemetry.semconv._incubating.attributes.otel_attributes import ( OtelComponentTypeValues, ) @@ -135,7 +138,10 @@ class PrometheusMetricReader(MetricReader): """Prometheus metric exporter for OpenTelemetry.""" def __init__( - self, disable_target_info: bool = False, prefix: str = "" + self, + disable_target_info: bool = False, + prefix: str = "", + default_aggregation: Optional[Dict[type, Aggregation]] = None, ) -> None: super().__init__( preferred_temporality={ @@ -146,6 +152,7 @@ def __init__( ObservableUpDownCounter: AggregationTemporality.CUMULATIVE, ObservableGauge: AggregationTemporality.CUMULATIVE, }, + preferred_aggregation=default_aggregation, otel_component_type=OtelComponentTypeValues.PROMETHEUS_HTTP_TEXT_METRIC_EXPORTER, ) self._collector = _CustomCollector( diff --git a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py index 26770c9e1f..19fc1654c4 100644 --- a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py +++ b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py @@ -719,3 +719,20 @@ def test_multiple_data_points_with_different_label_sets(self): """ ), ) + def test_default_aggregation(self): + """Test that default_aggregation parameter is passed to MetricReader.""" + from opentelemetry.sdk.metrics.view import ExplicitBucketHistogramAggregation + from opentelemetry.sdk.metrics import Histogram as HistogramInstrument + custom_aggregation = { + HistogramInstrument: ExplicitBucketHistogramAggregation( + boundaries=[1.0, 5.0, 10.0] + ) + } + reader = PrometheusMetricReader( + default_aggregation=custom_aggregation + ) + self.assertEqual( + reader._instrument_class_aggregation[HistogramInstrument].__class__, + ExplicitBucketHistogramAggregation, + ) + reader.shutdown() From 7496e2142d07d33fbaf084c2eb0c354cc27a5a08 Mon Sep 17 00:00:00 2001 From: manvi Date: Fri, 17 Apr 2026 08:54:02 +0530 Subject: [PATCH 2/6] fix(exporter/prometheus): move imports to top-level in test file --- .../tests/test_prometheus_exporter.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py index 19fc1654c4..24877c10c7 100644 --- a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py +++ b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py @@ -28,6 +28,7 @@ _CustomCollector, ) from opentelemetry.metrics import NoOpMeterProvider +from opentelemetry.sdk.metrics import Histogram as HistogramInstrument from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics.export import ( AggregationTemporality, @@ -38,6 +39,7 @@ ResourceMetrics, ScopeMetrics, ) +from opentelemetry.sdk.metrics.view import ExplicitBucketHistogramAggregation from opentelemetry.sdk.resources import Resource from opentelemetry.test.metrictestutil import ( _generate_gauge, @@ -719,10 +721,9 @@ def test_multiple_data_points_with_different_label_sets(self): """ ), ) + def test_default_aggregation(self): """Test that default_aggregation parameter is passed to MetricReader.""" - from opentelemetry.sdk.metrics.view import ExplicitBucketHistogramAggregation - from opentelemetry.sdk.metrics import Histogram as HistogramInstrument custom_aggregation = { HistogramInstrument: ExplicitBucketHistogramAggregation( boundaries=[1.0, 5.0, 10.0] @@ -732,7 +733,9 @@ def test_default_aggregation(self): default_aggregation=custom_aggregation ) self.assertEqual( - reader._instrument_class_aggregation[HistogramInstrument].__class__, + reader._instrument_class_aggregation[ + HistogramInstrument + ].__class__, ExplicitBucketHistogramAggregation, ) reader.shutdown() From a3a346d4296d1821bdd2d2f9fa5ad18ca112309e Mon Sep 17 00:00:00 2001 From: manvi Date: Fri, 17 Apr 2026 13:35:20 +0530 Subject: [PATCH 3/6] fix(exporter/prometheus): use modern type hint style for default_aggregation --- .../src/opentelemetry/exporter/prometheus/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 6525cecdc2..e611e879b9 100644 --- a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py +++ b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py @@ -67,7 +67,7 @@ from json import dumps from logging import getLogger from os import environ -from typing import Deque, Dict, Iterable, Optional, Sequence, Tuple, Union +from typing import Deque, Dict, Iterable, Sequence, Tuple, Union from prometheus_client import start_http_server from prometheus_client.core import ( @@ -141,7 +141,7 @@ def __init__( self, disable_target_info: bool = False, prefix: str = "", - default_aggregation: Optional[Dict[type, Aggregation]] = None, + default_aggregation: dict[type, Aggregation] | None = None, ) -> None: super().__init__( preferred_temporality={ From 43ac4095f3189ff529b5338b77f848213dc109db Mon Sep 17 00:00:00 2001 From: manvi Date: Sat, 18 Apr 2026 08:30:18 +0530 Subject: [PATCH 4/6] fix(exporter/prometheus): rename default_aggregation to preferred_aggregation --- .../exporter/prometheus/__init__.py | 4 +-- .../tests/test_prometheus_exporter.py | 32 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) 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 e611e879b9..47b16c70ed 100644 --- a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py +++ b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py @@ -141,7 +141,7 @@ def __init__( self, disable_target_info: bool = False, prefix: str = "", - default_aggregation: dict[type, Aggregation] | None = None, + preferred_aggregation: dict[type, Aggregation] | None = None, ) -> None: super().__init__( preferred_temporality={ @@ -152,7 +152,7 @@ def __init__( ObservableUpDownCounter: AggregationTemporality.CUMULATIVE, ObservableGauge: AggregationTemporality.CUMULATIVE, }, - preferred_aggregation=default_aggregation, + preferred_aggregation=preferred_aggregation, otel_component_type=OtelComponentTypeValues.PROMETHEUS_HTTP_TEXT_METRIC_EXPORTER, ) self._collector = _CustomCollector( diff --git a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py index 24877c10c7..4a67555cac 100644 --- a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py +++ b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py @@ -722,20 +722,20 @@ def test_multiple_data_points_with_different_label_sets(self): ), ) - def test_default_aggregation(self): - """Test that default_aggregation parameter is passed to MetricReader.""" - custom_aggregation = { - HistogramInstrument: ExplicitBucketHistogramAggregation( - boundaries=[1.0, 5.0, 10.0] - ) - } - reader = PrometheusMetricReader( - default_aggregation=custom_aggregation - ) - self.assertEqual( - reader._instrument_class_aggregation[ - HistogramInstrument - ].__class__, - ExplicitBucketHistogramAggregation, + def test_preferred_aggregation(self): + """Test that preferred_aggregation parameter is passed to MetricReader.""" + custom_aggregation = { + HistogramInstrument: ExplicitBucketHistogramAggregation( + boundaries=[1.0, 5.0, 10.0] ) - reader.shutdown() + } + reader = PrometheusMetricReader( + preferred_aggregation=custom_aggregation + ) + provider = MeterProvider(metric_readers=[reader]) + meter = provider.get_meter("test") + histogram = meter.create_histogram("test_histogram") + histogram.record(5) + result = list(reader._collector.collect()) + self.assertTrue(len(result) > 0) + reader.shutdown() From 9c9e127f92b53f5fbea97be726b7ab34a8220dae Mon Sep 17 00:00:00 2001 From: manvi Date: Sun, 19 Apr 2026 08:30:06 +0530 Subject: [PATCH 5/6] fix: update CHANGELOG to use PR number --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f456799ee1..a2479d79d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- `opentelemetry-exporter-prometheus`: Add `default_aggregation` parameter to `PrometheusMetricReader` to allow configuring default aggregation per instrument kind - ([#5109](https://github.com/open-telemetry/opentelemetry-python/issues/5109)) +- `opentelemetry-exporter-prometheus`: Add `preferred_aggregation` parameter to `PrometheusMetricReader` to allow configuring default aggregation per instrument kind + ([#5117](https://github.com/open-telemetry/opentelemetry-python/pull/5117)) - `opentelemetry-sdk`: fix YAML structure injection via environment variable substitution in declarative file configuration; values containing newlines are now emitted as quoted YAML scalars per spec requirement ([#5091](https://github.com/open-telemetry/opentelemetry-python/pull/5091)) - `opentelemetry-sdk`: Add `create_logger_provider`/`configure_logger_provider` to declarative file configuration, enabling LoggerProvider instantiation from config files without reading env vars From 4b05eb80fabab17bd6580ef1b37e8fa5a4342ad3 Mon Sep 17 00:00:00 2001 From: manvi Date: Thu, 23 Apr 2026 09:35:32 +0530 Subject: [PATCH 6/6] fix(exporter/prometheus): add boundary verification in test_preferred_aggregation --- .../tests/test_prometheus_exporter.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py index 4a67555cac..4162bd4f62 100644 --- a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py +++ b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py @@ -49,6 +49,7 @@ ) +# pylint: disable=too-many-public-methods class TestPrometheusMetricReader(TestCase): def setUp(self): self._mock_registry_register = Mock() @@ -738,4 +739,13 @@ def test_preferred_aggregation(self): histogram.record(5) result = list(reader._collector.collect()) self.assertTrue(len(result) > 0) + prometheus_metric = result[1] + bucket_bounds = [ + sample.labels["le"] + for sample in prometheus_metric.samples + if "le" in sample.labels + ] + self.assertIn("1.0", bucket_bounds) + self.assertIn("5.0", bucket_bounds) + self.assertIn("10.0", bucket_bounds) reader.shutdown()