Skip to content

Commit f0ac64f

Browse files
authored
Merge branch 'main' into pytyped
2 parents 5352b14 + c9277ff commit f0ac64f

File tree

4 files changed

+85
-8
lines changed

4 files changed

+85
-8
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10+
- Modify Prometheus exporter to translate non-monotonic Sums into Gauges
11+
([#3306](https://github.com/open-telemetry/opentelemetry-python/pull/3306))
12+
13+
1014
## Version 1.19.0/0.40b0 (2023-07-13)
1115

1216
- Drop `setuptools` runtime requirement.
@@ -91,7 +95,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9195
- Create a single resource instance
9296
([#3118](https://github.com/open-telemetry/opentelemetry-python/pull/3118))
9397

94-
9598
## Version 1.15.0/0.36b0 (2022-12-09)
9699

97100
- PeriodicExportingMetricsReader with +Inf interval

exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,25 @@ def _translate_to_prometheus(
263263
for pre_metric_family_id, label_values, value in zip(
264264
pre_metric_family_ids, label_valuess, values
265265
):
266-
if isinstance(metric.data, Sum):
266+
is_non_monotonic_sum = (
267+
isinstance(metric.data, Sum)
268+
and metric.data.is_monotonic is False
269+
)
270+
is_cumulative = (
271+
isinstance(metric.data, Sum)
272+
and metric.data.aggregation_temporality
273+
== AggregationTemporality.CUMULATIVE
274+
)
275+
276+
# The prometheus compatibility spec for sums says: If the aggregation temporality is cumulative and the sum is non-monotonic, it MUST be converted to a Prometheus Gauge.
277+
should_convert_sum_to_gauge = (
278+
is_non_monotonic_sum and is_cumulative
279+
)
280+
281+
if (
282+
isinstance(metric.data, Sum)
283+
and not should_convert_sum_to_gauge
284+
):
267285

268286
metric_family_id = "|".join(
269287
[pre_metric_family_id, CounterMetricFamily.__name__]
@@ -281,7 +299,10 @@ def _translate_to_prometheus(
281299
metric_family_id_metric_family[
282300
metric_family_id
283301
].add_metric(labels=label_values, value=value)
284-
elif isinstance(metric.data, Gauge):
302+
elif (
303+
isinstance(metric.data, Gauge)
304+
or should_convert_sum_to_gauge
305+
):
285306

286307
metric_family_id = "|".join(
287308
[pre_metric_family_id, GaugeMetricFamily.__name__]

exporter/opentelemetry-exporter-prometheus/tests/test_prometheus_exporter.py

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,10 @@ def test_histogram_to_prometheus(self):
125125
),
126126
)
127127

128-
def test_sum_to_prometheus(self):
128+
def test_monotonic_sum_to_prometheus(self):
129129
labels = {"environment@": "staging", "os": "Windows"}
130130
metric = _generate_sum(
131-
"test@sum",
131+
"test@sum_monotonic",
132132
123,
133133
attributes=labels,
134134
description="testdesc",
@@ -156,7 +156,55 @@ def test_sum_to_prometheus(self):
156156

157157
for prometheus_metric in collector.collect():
158158
self.assertEqual(type(prometheus_metric), CounterMetricFamily)
159-
self.assertEqual(prometheus_metric.name, "test_sum_testunit")
159+
self.assertEqual(
160+
prometheus_metric.name, "test_sum_monotonic_testunit"
161+
)
162+
self.assertEqual(prometheus_metric.documentation, "testdesc")
163+
self.assertTrue(len(prometheus_metric.samples) == 1)
164+
self.assertEqual(prometheus_metric.samples[0].value, 123)
165+
self.assertTrue(len(prometheus_metric.samples[0].labels) == 2)
166+
self.assertEqual(
167+
prometheus_metric.samples[0].labels["environment_"], "staging"
168+
)
169+
self.assertEqual(
170+
prometheus_metric.samples[0].labels["os"], "Windows"
171+
)
172+
173+
def test_non_monotonic_sum_to_prometheus(self):
174+
labels = {"environment@": "staging", "os": "Windows"}
175+
metric = _generate_sum(
176+
"test@sum_nonmonotonic",
177+
123,
178+
attributes=labels,
179+
description="testdesc",
180+
unit="testunit",
181+
is_monotonic=False,
182+
)
183+
184+
metrics_data = MetricsData(
185+
resource_metrics=[
186+
ResourceMetrics(
187+
resource=Mock(),
188+
scope_metrics=[
189+
ScopeMetrics(
190+
scope=Mock(),
191+
metrics=[metric],
192+
schema_url="schema_url",
193+
)
194+
],
195+
schema_url="schema_url",
196+
)
197+
]
198+
)
199+
200+
collector = _CustomCollector(disable_target_info=True)
201+
collector.add_metrics_data(metrics_data)
202+
203+
for prometheus_metric in collector.collect():
204+
self.assertEqual(type(prometheus_metric), GaugeMetricFamily)
205+
self.assertEqual(
206+
prometheus_metric.name, "test_sum_nonmonotonic_testunit"
207+
)
160208
self.assertEqual(prometheus_metric.documentation, "testdesc")
161209
self.assertTrue(len(prometheus_metric.samples) == 1)
162210
self.assertEqual(prometheus_metric.samples[0].value, 123)

tests/opentelemetry-test-utils/src/opentelemetry/test/metrictestutil.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ def _generate_metric(
3939

4040

4141
def _generate_sum(
42-
name, value, attributes=None, description=None, unit=None
42+
name,
43+
value,
44+
attributes=None,
45+
description=None,
46+
unit=None,
47+
is_monotonic=True,
4348
) -> Metric:
4449
if attributes is None:
4550
attributes = BoundedAttributes(attributes={"a": 1, "b": True})
@@ -55,7 +60,7 @@ def _generate_sum(
5560
)
5661
],
5762
aggregation_temporality=AggregationTemporality.CUMULATIVE,
58-
is_monotonic=True,
63+
is_monotonic=is_monotonic,
5964
),
6065
description=description,
6166
unit=unit,

0 commit comments

Comments
 (0)