Skip to content

Commit 01fc016

Browse files
brettimusBrett Beutell
authored andcommitted
Modify Prometheus exporter to translate non-monotonic Sums into gauges
Add unit tests for translating monotonic and non-monotonic Sums to prometheus metrics Update prometheus metric translation to match spec Added a check for the aggregation temporality of a Sum. "If the aggregation temporality is cumulative and the sum is non-monotonic, it MUST be converted to a Prometheus Gauge." Update changelog
1 parent 3732fd4 commit 01fc016

File tree

4 files changed

+59
-8
lines changed

4 files changed

+59
-8
lines changed

CHANGELOG.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ 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))
1012
- Select histogram aggregation with an environment variable
1113
([#3265](https://github.com/open-telemetry/opentelemetry-python/pull/3265))
1214
- Move Protobuf encoding to its own package
@@ -41,7 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4143

4244
## Version 1.16.0/0.37b0 (2023-02-17)
4345

44-
- Change ``__all__`` to be statically defined.
46+
- Change `__all__` to be statically defined.
4547
([#3143](https://github.com/open-telemetry/opentelemetry-python/pull/3143))
4648
- Remove the ability to set a global metric prefix for Prometheus exporter
4749
([#3137](https://github.com/open-telemetry/opentelemetry-python/pull/3137))
@@ -64,7 +66,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6466
- Create a single resource instance
6567
([#3118](https://github.com/open-telemetry/opentelemetry-python/pull/3118))
6668

67-
6869
## Version 1.15.0/0.36b0 (2022-12-09)
6970

7071
- PeriodicExportingMetricsReader with +Inf interval
@@ -82,7 +83,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8283
([#3027](https://github.com/open-telemetry/opentelemetry-python/pull/3027))
8384
- Update logging to include logging api as per specification
8485
([#3038](https://github.com/open-telemetry/opentelemetry-python/pull/3038))
85-
- Fix: Avoid generator in metrics _ViewInstrumentMatch.collect()
86+
- Fix: Avoid generator in metrics \_ViewInstrumentMatch.collect()
8687
([#3035](https://github.com/open-telemetry/opentelemetry-python/pull/3035)
8788
- [exporter-otlp-proto-grpc] add user agent string
8889
([#3009](https://github.com/open-telemetry/opentelemetry-python/pull/3009))

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,13 @@ def _translate_to_prometheus(
244244
for pre_metric_family_id, label_values, value in zip(
245245
pre_metric_family_ids, label_valuess, values
246246
):
247-
if isinstance(metric.data, Sum):
247+
is_non_monotonic_sum = isinstance(metric.data, Sum) and metric.data.is_monotonic == False
248+
is_cumulative = isinstance(metric.data, Sum) and metric.data.aggregation_temporality == AggregationTemporality.CUMULATIVE
249+
250+
# 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.
251+
should_convert_sum_to_gauge = (is_non_monotonic_sum and is_cumulative)
252+
253+
if isinstance(metric.data, Sum) and not should_convert_sum_to_gauge:
248254

249255
metric_family_id = "|".join(
250256
[pre_metric_family_id, CounterMetricFamily.__name__]
@@ -262,7 +268,7 @@ def _translate_to_prometheus(
262268
metric_family_id_metric_family[
263269
metric_family_id
264270
].add_metric(labels=label_values, value=value)
265-
elif isinstance(metric.data, Gauge):
271+
elif isinstance(metric.data, Gauge) or should_convert_sum_to_gauge:
266272

267273
metric_family_id = "|".join(
268274
[pre_metric_family_id, GaugeMetricFamily.__name__]

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def test_histogram_to_prometheus(self):
120120
),
121121
)
122122

123-
def test_sum_to_prometheus(self):
123+
def test_monotonic_sum_to_prometheus(self):
124124
labels = {"environment@": "staging", "os": "Windows"}
125125
metric = _generate_sum(
126126
"test@sum",
@@ -163,6 +163,50 @@ def test_sum_to_prometheus(self):
163163
prometheus_metric.samples[0].labels["os"], "Windows"
164164
)
165165

166+
def test_non_monotonic_sum_to_prometheus(self):
167+
labels = {"environment@": "staging", "os": "Windows"}
168+
metric = _generate_sum(
169+
"test@sum",
170+
123,
171+
attributes=labels,
172+
description="testdesc",
173+
unit="testunit",
174+
is_monotonic=False
175+
)
176+
177+
metrics_data = MetricsData(
178+
resource_metrics=[
179+
ResourceMetrics(
180+
resource=Mock(),
181+
scope_metrics=[
182+
ScopeMetrics(
183+
scope=Mock(),
184+
metrics=[metric],
185+
schema_url="schema_url",
186+
)
187+
],
188+
schema_url="schema_url",
189+
)
190+
]
191+
)
192+
193+
collector = _CustomCollector()
194+
collector.add_metrics_data(metrics_data)
195+
196+
for prometheus_metric in collector.collect():
197+
self.assertEqual(type(prometheus_metric), GaugeMetricFamily)
198+
self.assertEqual(prometheus_metric.name, "test_sum_testunit")
199+
self.assertEqual(prometheus_metric.documentation, "testdesc")
200+
self.assertTrue(len(prometheus_metric.samples) == 1)
201+
self.assertEqual(prometheus_metric.samples[0].value, 123)
202+
self.assertTrue(len(prometheus_metric.samples[0].labels) == 2)
203+
self.assertEqual(
204+
prometheus_metric.samples[0].labels["environment_"], "staging"
205+
)
206+
self.assertEqual(
207+
prometheus_metric.samples[0].labels["os"], "Windows"
208+
)
209+
166210
def test_gauge_to_prometheus(self):
167211
labels = {"environment@": "dev", "os": "Unix"}
168212
metric = _generate_gauge(

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

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

4040

4141
def _generate_sum(
42-
name, value, attributes=None, description=None, unit=None
42+
name, value, attributes=None, description=None, unit=None, is_monotonic=True
4343
) -> Metric:
4444
if attributes is None:
4545
attributes = BoundedAttributes(attributes={"a": 1, "b": True})
@@ -55,7 +55,7 @@ def _generate_sum(
5555
)
5656
],
5757
aggregation_temporality=AggregationTemporality.CUMULATIVE,
58-
is_monotonic=True,
58+
is_monotonic=is_monotonic,
5959
),
6060
description=description,
6161
unit=unit,

0 commit comments

Comments
 (0)