From a16f6b72ed340cc7ae4f56420399ee3bc83b8f82 Mon Sep 17 00:00:00 2001 From: Lukas Hering Date: Mon, 13 Jul 2026 22:18:46 -0400 Subject: [PATCH 1/2] opentelemetry-exporter-prometheus: add preferred_aggregation parameter to PrometheusMetricReader --- .changelog/xxxx.added | 1 + .../exporter/prometheus/__init__.py | 12 ++++++- .../tests/test_prometheus_exporter.py | 32 +++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 .changelog/xxxx.added diff --git a/.changelog/xxxx.added b/.changelog/xxxx.added new file mode 100644 index 00000000000..64c43ee96c3 --- /dev/null +++ b/.changelog/xxxx.added @@ -0,0 +1 @@ +`opentelemetry-exporter-prometheus`: Add `preferred_aggregation` parameter to `PrometheusMetricReader` 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 12a6755337f..a29f7593a51 100644 --- a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py +++ b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py @@ -52,7 +52,7 @@ """ from collections import deque -from collections.abc import Callable, Iterable, Sequence +from collections.abc import Callable, Iterable, Mapping, Sequence from itertools import chain from json import dumps from logging import getLogger @@ -100,6 +100,7 @@ MetricsData, Sum, ) +from opentelemetry.sdk.metrics.view import Aggregation from opentelemetry.sdk.util.instrumentation import InstrumentationScope from opentelemetry.semconv._incubating.attributes.otel_attributes import ( OtelComponentTypeValues, @@ -255,6 +256,11 @@ class PrometheusMetricReader(MetricReader): scope_info_enabled: Whether to include instrumentation scope labels on exported metrics. Scope labels are exported by default. prefix: Prefix added to exported Prometheus metric names. + preferred_aggregation: A mapping between instrument classes and + aggregation instances used to override the default aggregation of + the corresponding instrument classes. Classes not included in the + mapping retain their default aggregation. See + :class:`~opentelemetry.sdk.metrics.export.MetricReader` for details. """ def __init__( @@ -262,6 +268,7 @@ def __init__( disable_target_info: bool = False, prefix: str = "", scope_info_enabled: bool = True, + preferred_aggregation: Mapping[type, Aggregation] | None = None, *, registry: CollectorRegistry = REGISTRY, ) -> None: @@ -274,6 +281,9 @@ def __init__( ObservableUpDownCounter: AggregationTemporality.CUMULATIVE, ObservableGauge: AggregationTemporality.CUMULATIVE, }, + preferred_aggregation=dict(preferred_aggregation) + if preferred_aggregation is not None + else None, 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 24a1ab284e5..dde70ca090d 100644 --- a/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py +++ b/exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py @@ -21,6 +21,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, @@ -31,6 +32,7 @@ ResourceMetrics, ScopeMetrics, ) +from opentelemetry.sdk.metrics.view import ExplicitBucketHistogramAggregation from opentelemetry.sdk.resources import Resource from opentelemetry.sdk.util.instrumentation import InstrumentationScope from opentelemetry.test.metrictestutil import ( @@ -920,3 +922,33 @@ def test_multiple_data_points_with_different_label_sets(self): """ ), ) + + def test_preferred_aggregation(self): + custom_aggregation = { + HistogramInstrument: ExplicitBucketHistogramAggregation( + boundaries=[1.0, 5.0, 10.0] + ) + } + reader = PrometheusMetricReader( + preferred_aggregation=custom_aggregation, + registry=CollectorRegistry(), + ) + provider = MeterProvider( + metric_readers=[reader], shutdown_on_exit=False + ) + meter = provider.get_meter("test") + histogram = meter.create_histogram("test_histogram") + histogram.record(5) + + result = list(reader._collector.collect()) + self.assertTrue(result) + bucket_bounds = [ + sample.labels["le"] + for metric_family in result + for sample in metric_family.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() From 861ade49e89b85eaa2851833b2e222df5f34e90a Mon Sep 17 00:00:00 2001 From: Lukas Hering Date: Mon, 13 Jul 2026 22:21:20 -0400 Subject: [PATCH 2/2] update changelog fragment number --- .changelog/{xxxx.added => 5411.added} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changelog/{xxxx.added => 5411.added} (100%) diff --git a/.changelog/xxxx.added b/.changelog/5411.added similarity index 100% rename from .changelog/xxxx.added rename to .changelog/5411.added