Repository: open-telemetry/opentelemetry-python

Issue:
OTLP Exporter: Add support for Histogram point types



Discussion:

Resolving patch:
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_metric_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_metric_exporter/__init__.py
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_metric_exporter/__init__.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/_metric_exporter/__init__.py
@@ -132,10 +132,18 @@ def _translate_data(
                     pt.as_double = metric.point.value
                 pbmetric.gauge.data_points.append(pt)
             elif isinstance(metric.point, Histogram):
-                # TODO: implement histogram
-                pbmetric.histogram = pb2.Histogram(
-                    data_points=[],
+                pt = pb2.HistogramDataPoint(
+                    attributes=self._translate_attributes(metric.attributes),
+                    time_unix_nano=metric.point.time_unix_nano,
+                    start_time_unix_nano=metric.point.start_time_unix_nano,
+                    count=sum(metric.point.bucket_counts),
+                    bucket_counts=metric.point.bucket_counts,
+                    explicit_bounds=metric.point.explicit_bounds,
+                )
+                pbmetric.histogram.aggregation_temporality = (
+                    metric.point.aggregation_temporality
                 )
+                pbmetric.histogram.data_points.append(pt)
             elif isinstance(metric.point, Sum):
                 pt = pb2.NumberDataPoint(
                     attributes=self._translate_attributes(metric.attributes),


Test patch:
diff --git a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/metrics/test_otlp_metrics_exporter.py b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/metrics/test_otlp_metrics_exporter.py
--- a/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/metrics/test_otlp_metrics_exporter.py
+++ b/exporter/opentelemetry-exporter-otlp-proto-grpc/tests/metrics/test_otlp_metrics_exporter.py
@@ -46,6 +46,7 @@
 from opentelemetry.sdk._metrics.point import (
     AggregationTemporality,
     Gauge,
+    Histogram,
     Metric,
     Sum,
 )
@@ -152,9 +153,20 @@ def setUp(self):
 
         self.metrics = {
             "sum_int": _generate_sum("sum_int", 33),
-            "sum_float": _generate_sum("sum_float", 2.98),
+            "sum_double": _generate_sum("sum_double", 2.98),
             "gauge_int": _generate_gauge("gauge_int", 9000),
-            "gauge_float": _generate_gauge("gauge_float", 52.028),
+            "gauge_double": _generate_gauge("gauge_double", 52.028),
+            "histogram": _generate_metric(
+                "histogram",
+                Histogram(
+                    time_unix_nano=1641946016139533244,
+                    start_time_unix_nano=1641946016139533244,
+                    bucket_counts=[1, 4],
+                    sum=67,
+                    explicit_bounds=[10.0, 20.0],
+                    aggregation_temporality=AggregationTemporality.DELTA,
+                ),
+            ),
         }
 
     def tearDown(self):
@@ -367,7 +379,7 @@ def test_translate_sum_int(self):
         actual = self.exporter._translate_data([self.metrics["sum_int"]])
         self.assertEqual(expected, actual)
 
-    def test_translate_sum_float(self):
+    def test_translate_sum_double(self):
         expected = ExportMetricsServiceRequest(
             resource_metrics=[
                 pb2.ResourceMetrics(
@@ -386,7 +398,7 @@ def test_translate_sum_float(self):
                             ),
                             metrics=[
                                 pb2.Metric(
-                                    name="sum_float",
+                                    name="sum_double",
                                     unit="s",
                                     description="foo",
                                     sum=pb2.Sum(
@@ -422,7 +434,7 @@ def test_translate_sum_float(self):
             ]
         )
         # pylint: disable=protected-access
-        actual = self.exporter._translate_data([self.metrics["sum_float"]])
+        actual = self.exporter._translate_data([self.metrics["sum_double"]])
         self.assertEqual(expected, actual)
 
     def test_translate_gauge_int(self):
@@ -480,7 +492,7 @@ def test_translate_gauge_int(self):
         actual = self.exporter._translate_data([self.metrics["gauge_int"]])
         self.assertEqual(expected, actual)
 
-    def test_translate_gauge_float(self):
+    def test_translate_gauge_double(self):
         expected = ExportMetricsServiceRequest(
             resource_metrics=[
                 pb2.ResourceMetrics(
@@ -499,7 +511,7 @@ def test_translate_gauge_float(self):
                             ),
                             metrics=[
                                 pb2.Metric(
-                                    name="gauge_float",
+                                    name="gauge_double",
                                     unit="s",
                                     description="foo",
                                     gauge=pb2.Gauge(
@@ -532,5 +544,66 @@ def test_translate_gauge_float(self):
             ]
         )
         # pylint: disable=protected-access
-        actual = self.exporter._translate_data([self.metrics["gauge_float"]])
+        actual = self.exporter._translate_data([self.metrics["gauge_double"]])
+        self.assertEqual(expected, actual)
+
+    def test_translate_histogram(self):
+        expected = ExportMetricsServiceRequest(
+            resource_metrics=[
+                pb2.ResourceMetrics(
+                    resource=OTLPResource(
+                        attributes=[
+                            KeyValue(key="a", value=AnyValue(int_value=1)),
+                            KeyValue(
+                                key="b", value=AnyValue(bool_value=False)
+                            ),
+                        ]
+                    ),
+                    instrumentation_library_metrics=[
+                        pb2.InstrumentationLibraryMetrics(
+                            instrumentation_library=InstrumentationLibrary(
+                                name="first_name", version="first_version"
+                            ),
+                            metrics=[
+                                pb2.Metric(
+                                    name="histogram",
+                                    unit="s",
+                                    description="foo",
+                                    histogram=pb2.Histogram(
+                                        data_points=[
+                                            pb2.HistogramDataPoint(
+                                                attributes=[
+                                                    KeyValue(
+                                                        key="a",
+                                                        value=AnyValue(
+                                                            int_value=1
+                                                        ),
+                                                    ),
+                                                    KeyValue(
+                                                        key="b",
+                                                        value=AnyValue(
+                                                            bool_value=True
+                                                        ),
+                                                    ),
+                                                ],
+                                                start_time_unix_nano=1641946016139533244,
+                                                time_unix_nano=1641946016139533244,
+                                                count=5,
+                                                bucket_counts=[1, 4],
+                                                explicit_bounds=[10.0, 20.0],
+                                                exemplars=[],
+                                                flags=pb2.DataPointFlags.FLAG_NONE,
+                                            )
+                                        ],
+                                        aggregation_temporality=AggregationTemporality.DELTA,
+                                    ),
+                                )
+                            ],
+                        )
+                    ],
+                )
+            ]
+        )
+        # pylint: disable=protected-access
+        actual = self.exporter._translate_data([self.metrics["histogram"]])
         self.assertEqual(expected, actual)