Skip to content

Commit

Permalink
Modify Prometheus exporter to translate non-monotonic Sums into gauges (
Browse files Browse the repository at this point in the history
#3306)

Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
  • Loading branch information
brettimus and srikanthccv committed Aug 7, 2023
1 parent b9f31e9 commit c9277ff
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 8 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

- Modify Prometheus exporter to translate non-monotonic Sums into Gauges
([#3306](https://github.com/open-telemetry/opentelemetry-python/pull/3306))


## Version 1.19.0/0.40b0 (2023-07-13)

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


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

- PeriodicExportingMetricsReader with +Inf interval
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,25 @@ def _translate_to_prometheus(
for pre_metric_family_id, label_values, value in zip(
pre_metric_family_ids, label_valuess, values
):
if isinstance(metric.data, Sum):
is_non_monotonic_sum = (
isinstance(metric.data, Sum)
and metric.data.is_monotonic is False
)
is_cumulative = (
isinstance(metric.data, Sum)
and metric.data.aggregation_temporality
== AggregationTemporality.CUMULATIVE
)

# 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.
should_convert_sum_to_gauge = (
is_non_monotonic_sum and is_cumulative
)

if (
isinstance(metric.data, Sum)
and not should_convert_sum_to_gauge
):

metric_family_id = "|".join(
[pre_metric_family_id, CounterMetricFamily.__name__]
Expand All @@ -281,7 +299,10 @@ def _translate_to_prometheus(
metric_family_id_metric_family[
metric_family_id
].add_metric(labels=label_values, value=value)
elif isinstance(metric.data, Gauge):
elif (
isinstance(metric.data, Gauge)
or should_convert_sum_to_gauge
):

metric_family_id = "|".join(
[pre_metric_family_id, GaugeMetricFamily.__name__]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,10 @@ def test_histogram_to_prometheus(self):
),
)

def test_sum_to_prometheus(self):
def test_monotonic_sum_to_prometheus(self):
labels = {"environment@": "staging", "os": "Windows"}
metric = _generate_sum(
"test@sum",
"test@sum_monotonic",
123,
attributes=labels,
description="testdesc",
Expand Down Expand Up @@ -156,7 +156,55 @@ def test_sum_to_prometheus(self):

for prometheus_metric in collector.collect():
self.assertEqual(type(prometheus_metric), CounterMetricFamily)
self.assertEqual(prometheus_metric.name, "test_sum_testunit")
self.assertEqual(
prometheus_metric.name, "test_sum_monotonic_testunit"
)
self.assertEqual(prometheus_metric.documentation, "testdesc")
self.assertTrue(len(prometheus_metric.samples) == 1)
self.assertEqual(prometheus_metric.samples[0].value, 123)
self.assertTrue(len(prometheus_metric.samples[0].labels) == 2)
self.assertEqual(
prometheus_metric.samples[0].labels["environment_"], "staging"
)
self.assertEqual(
prometheus_metric.samples[0].labels["os"], "Windows"
)

def test_non_monotonic_sum_to_prometheus(self):
labels = {"environment@": "staging", "os": "Windows"}
metric = _generate_sum(
"test@sum_nonmonotonic",
123,
attributes=labels,
description="testdesc",
unit="testunit",
is_monotonic=False,
)

metrics_data = MetricsData(
resource_metrics=[
ResourceMetrics(
resource=Mock(),
scope_metrics=[
ScopeMetrics(
scope=Mock(),
metrics=[metric],
schema_url="schema_url",
)
],
schema_url="schema_url",
)
]
)

collector = _CustomCollector(disable_target_info=True)
collector.add_metrics_data(metrics_data)

for prometheus_metric in collector.collect():
self.assertEqual(type(prometheus_metric), GaugeMetricFamily)
self.assertEqual(
prometheus_metric.name, "test_sum_nonmonotonic_testunit"
)
self.assertEqual(prometheus_metric.documentation, "testdesc")
self.assertTrue(len(prometheus_metric.samples) == 1)
self.assertEqual(prometheus_metric.samples[0].value, 123)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ def _generate_metric(


def _generate_sum(
name, value, attributes=None, description=None, unit=None
name,
value,
attributes=None,
description=None,
unit=None,
is_monotonic=True,
) -> Metric:
if attributes is None:
attributes = BoundedAttributes(attributes={"a": 1, "b": True})
Expand All @@ -55,7 +60,7 @@ def _generate_sum(
)
],
aggregation_temporality=AggregationTemporality.CUMULATIVE,
is_monotonic=True,
is_monotonic=is_monotonic,
),
description=description,
unit=unit,
Expand Down

0 comments on commit c9277ff

Please sign in to comment.