From 6968f1572886bccd1c9bf52623eee59eca2cb5cd Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Wed, 13 Mar 2024 13:24:23 -0700 Subject: [PATCH 1/4] Add to_json method to ExponentialHistogram --- .../src/opentelemetry/sdk/metrics/_internal/point.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/point.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/point.py index c30705c59a4..42420b9008e 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/point.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/point.py @@ -101,6 +101,18 @@ class ExponentialHistogram: "opentelemetry.sdk.metrics.export.AggregationTemporality" ) + def to_json(self, indent=4) -> str: + return dumps( + { + "data_points": [ + loads(data_point.to_json(indent=indent)) + for data_point in self.data_points + ], + "aggregation_temporality": self.aggregation_temporality, + }, + indent=indent, + ) + @dataclass(frozen=True) class Sum: From ba090dd33885098fa61de6f43c42a3c206d93244 Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Thu, 14 Mar 2024 11:30:04 -0700 Subject: [PATCH 2/4] Add ExponentialBucketHistogram test to the exporter --- .../integration_test/test_console_exporter.py | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py b/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py index 1b3283717ae..e67429df6a2 100644 --- a/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py +++ b/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py @@ -16,12 +16,16 @@ from json import loads from unittest import TestCase -from opentelemetry.metrics import get_meter, set_meter_provider +from opentelemetry.metrics import Histogram, get_meter, set_meter_provider from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics.export import ( ConsoleMetricExporter, PeriodicExportingMetricReader, ) +from opentelemetry.sdk.metrics.view import ( + ExponentialBucketHistogramAggregation, + View, +) from opentelemetry.test.globals_test import reset_metrics_globals @@ -73,6 +77,56 @@ def test_console_exporter(self): self.assertEqual(metrics["attributes"], {"a": "b"}) self.assertEqual(metrics["value"], 1) + def test_exp_histogram_exporter(self): + output = StringIO() + exporter = ConsoleMetricExporter(out=output) + reader = PeriodicExportingMetricReader( + exporter, export_interval_millis=100 + ) + provider = MeterProvider( + metric_readers=[reader], + views=[ + View( + instrument_type=Histogram, + aggregation=ExponentialBucketHistogramAggregation(), + ), + ], + ) + set_meter_provider(provider) + meter = get_meter(__name__) + hist = meter.create_histogram( + "name", description="description", unit="unit" + ) + hist.record(1, attributes={"a": "b"}) + provider.shutdown() + + output.seek(0) + result_0 = loads("".join(output.readlines())) + + self.assertGreater(len(result_0), 0) + + metrics = result_0["resource_metrics"][0]["scope_metrics"][0] + + self.assertEqual(metrics["scope"]["name"], "test_console_exporter") + + metrics = metrics["metrics"][0] + + self.assertEqual(metrics["name"], "name") + self.assertEqual(metrics["description"], "description") + self.assertEqual(metrics["unit"], "unit") + + metrics = metrics["data"] + + self.assertEqual(metrics["aggregation_temporality"], 2) + self.assertEqual(len(metrics["data_points"]), 1) + + metrics = metrics["data_points"][0] + + self.assertEqual(metrics["attributes"], {"a": "b"}) + self.assertEqual(metrics["count"], 1) + self.assertEqual(metrics["sum"], 1) + self.assertEqual(metrics["zero_count"], 0) + def test_console_exporter_no_export(self): output = StringIO() From 4aecf46c3e01cfd3a23730e6820dd3d0669c0635 Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Mon, 18 Mar 2024 19:56:03 -0700 Subject: [PATCH 3/4] Add changelog entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50e6f2125d1..42a0bb4d354 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix python 3.12 deprecation warning ([#3751](https://github.com/open-telemetry/opentelemetry-python/pull/3751)) +- Add to_json method to ExponentialHistogram + ([#3780](https://github.com/open-telemetry/opentelemetry-python/pull/3780)) - bump mypy to 0.982 ([#3776](https://github.com/open-telemetry/opentelemetry-python/pull/3776)) - Fix ValueError message for PeriodicExportingMetricsReader From 3f67ec9963d73fddc496973d36a178b1ba2d736a Mon Sep 17 00:00:00 2001 From: Alexey Ivanov Date: Fri, 22 Mar 2024 12:14:55 -0700 Subject: [PATCH 4/4] Move exponential histogram tests from integration to unit --- .../integration_test/test_console_exporter.py | 56 +------------------ opentelemetry-sdk/tests/metrics/test_point.py | 44 +++++++++++++++ 2 files changed, 45 insertions(+), 55 deletions(-) diff --git a/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py b/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py index e67429df6a2..1b3283717ae 100644 --- a/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py +++ b/opentelemetry-sdk/tests/metrics/integration_test/test_console_exporter.py @@ -16,16 +16,12 @@ from json import loads from unittest import TestCase -from opentelemetry.metrics import Histogram, get_meter, set_meter_provider +from opentelemetry.metrics import get_meter, set_meter_provider from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics.export import ( ConsoleMetricExporter, PeriodicExportingMetricReader, ) -from opentelemetry.sdk.metrics.view import ( - ExponentialBucketHistogramAggregation, - View, -) from opentelemetry.test.globals_test import reset_metrics_globals @@ -77,56 +73,6 @@ def test_console_exporter(self): self.assertEqual(metrics["attributes"], {"a": "b"}) self.assertEqual(metrics["value"], 1) - def test_exp_histogram_exporter(self): - output = StringIO() - exporter = ConsoleMetricExporter(out=output) - reader = PeriodicExportingMetricReader( - exporter, export_interval_millis=100 - ) - provider = MeterProvider( - metric_readers=[reader], - views=[ - View( - instrument_type=Histogram, - aggregation=ExponentialBucketHistogramAggregation(), - ), - ], - ) - set_meter_provider(provider) - meter = get_meter(__name__) - hist = meter.create_histogram( - "name", description="description", unit="unit" - ) - hist.record(1, attributes={"a": "b"}) - provider.shutdown() - - output.seek(0) - result_0 = loads("".join(output.readlines())) - - self.assertGreater(len(result_0), 0) - - metrics = result_0["resource_metrics"][0]["scope_metrics"][0] - - self.assertEqual(metrics["scope"]["name"], "test_console_exporter") - - metrics = metrics["metrics"][0] - - self.assertEqual(metrics["name"], "name") - self.assertEqual(metrics["description"], "description") - self.assertEqual(metrics["unit"], "unit") - - metrics = metrics["data"] - - self.assertEqual(metrics["aggregation_temporality"], 2) - self.assertEqual(len(metrics["data_points"]), 1) - - metrics = metrics["data_points"][0] - - self.assertEqual(metrics["attributes"], {"a": "b"}) - self.assertEqual(metrics["count"], 1) - self.assertEqual(metrics["sum"], 1) - self.assertEqual(metrics["zero_count"], 0) - def test_console_exporter_no_export(self): output = StringIO() diff --git a/opentelemetry-sdk/tests/metrics/test_point.py b/opentelemetry-sdk/tests/metrics/test_point.py index 5d6640fdea6..20dd0e72384 100644 --- a/opentelemetry-sdk/tests/metrics/test_point.py +++ b/opentelemetry-sdk/tests/metrics/test_point.py @@ -16,6 +16,9 @@ from opentelemetry.sdk.metrics.export import ( AggregationTemporality, + Buckets, + ExponentialHistogram, + ExponentialHistogramDataPoint, Gauge, Histogram, HistogramDataPoint, @@ -100,6 +103,22 @@ def setUpClass(cls): ) cls.histogram_data_point_1_str = f'{{"attributes": {cls.attributes_1_str}, "start_time_unix_nano": 2, "time_unix_nano": 3, "count": 4, "sum": 4.4, "bucket_counts": [2, 1, 1], "explicit_bounds": [1.2, 2.3, 3.4, 4.5], "min": 0.3, "max": 4.4}}' + cls.exp_histogram_data_point_0 = ExponentialHistogramDataPoint( + attributes=cls.attributes_0, + start_time_unix_nano=1, + time_unix_nano=2, + count=1, + sum=10, + scale=1, + zero_count=0, + positive=Buckets(offset=0, bucket_counts=[1]), + negative=Buckets(offset=0, bucket_counts=[0]), + flags=0, + min=10, + max=10, + ) + cls.exp_histogram_data_point_0_str = f'{{"attributes": {cls.attributes_0_str}, "start_time_unix_nano": 1, "time_unix_nano": 2, "count": 1, "sum": 10, "scale": 1, "zero_count": 0, "positive": {{"offset": 0, "bucket_counts": [1]}}, "negative": {{"offset": 0, "bucket_counts": [0]}}, "flags": 0, "min": 10, "max": 10}}' + cls.sum_0 = Sum( data_points=[cls.number_data_point_0, cls.number_data_point_1], aggregation_temporality=AggregationTemporality.DELTA, @@ -121,6 +140,14 @@ def setUpClass(cls): ) cls.histogram_0_str = f'{{"data_points": [{cls.histogram_data_point_0_str}, {cls.histogram_data_point_1_str}], "aggregation_temporality": 1}}' + cls.exp_histogram_0 = ExponentialHistogram( + data_points=[ + cls.exp_histogram_data_point_0, + ], + aggregation_temporality=AggregationTemporality.CUMULATIVE, + ) + cls.exp_histogram_0_str = f'{{"data_points": [{cls.exp_histogram_data_point_0_str}], "aggregation_temporality": 2}}' + cls.metric_0 = Metric( name="metric_0", description="description_0", @@ -209,6 +236,15 @@ def test_histogram_data_point(self): self.histogram_data_point_1_str, ) + def test_exp_histogram_data_point(self): + + self.maxDiff = None + + self.assertEqual( + self.exp_histogram_data_point_0.to_json(indent=None), + self.exp_histogram_data_point_0_str, + ) + def test_sum(self): self.assertEqual(self.sum_0.to_json(indent=None), self.sum_0_str) @@ -225,6 +261,14 @@ def test_histogram(self): self.histogram_0.to_json(indent=None), self.histogram_0_str ) + def test_exp_histogram(self): + + self.maxDiff = None + + self.assertEqual( + self.exp_histogram_0.to_json(indent=None), self.exp_histogram_0_str + ) + def test_metric(self): self.assertEqual(self.metric_0.to_json(indent=None), self.metric_0_str)