From 9e78bcb029f40c5801a13a38368b56dd71658cac Mon Sep 17 00:00:00 2001 From: Aaron Abbott Date: Wed, 12 Jan 2022 13:45:25 -0500 Subject: [PATCH] Complete metric exporter format and update OTLP exporter (#2364) --- CHANGELOG.md | 2 + .../proto/grpc/_metric_exporter/__init__.py | 30 +++++++-------- .../metrics/test_otlp_metrics_exporter.py | 23 ++++++++--- .../opentelemetry/sdk/_metrics/aggregation.py | 16 ++++---- .../src/opentelemetry/sdk/_metrics/data.py | 35 ----------------- .../sdk/_metrics/export/__init__.py | 11 +++--- .../src/opentelemetry/sdk/_metrics/point.py | 38 ++++++++++++++++++- 7 files changed, 83 insertions(+), 72 deletions(-) delete mode 100644 opentelemetry-sdk/src/opentelemetry/sdk/_metrics/data.py diff --git a/CHANGELOG.md b/CHANGELOG.md index c91835f1d2d..85fd2794c0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#2312](https://github.com/open-telemetry/opentelemetry-python/pull/2312)) - [exporter/opentelemetry-exporter-otlp-proto-grpc] Add OTLPMetricExporter ([#2323](https://github.com/open-telemetry/opentelemetry-python/pull/2323)) +- Complete metric exporter format and update OTLP exporter + ([#2364](https://github.com/open-telemetry/opentelemetry-python/pull/2364)) ## [1.8.0-0.27b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.8.0-0.27b0) - 2021-12-17 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 index 9db2699da76..d31aebfb3fb 100644 --- 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 @@ -33,8 +33,8 @@ from opentelemetry.sdk.environment_variables import ( OTEL_EXPORTER_OTLP_METRICS_INSECURE, ) -from opentelemetry.sdk._metrics.data import ( - MetricData, +from opentelemetry.sdk._metrics.point import ( + Metric, ) from opentelemetry.sdk._metrics.export import ( @@ -45,9 +45,7 @@ class OTLPMetricExporter( MetricExporter, - OTLPExporterMixin[ - MetricData, ExportMetricsServiceRequest, MetricExportResult - ], + OTLPExporterMixin[Metric, ExportMetricsServiceRequest, MetricExportResult], ): _result = MetricExportResult _stub = MetricsServiceStub @@ -79,13 +77,13 @@ def __init__( ) def _translate_data( - self, data: Sequence[MetricData] + self, data: Sequence[Metric] ) -> ExportMetricsServiceRequest: sdk_resource_instrumentation_library_metrics = {} self._collector_metric_kwargs = {} - for metric_data in data: - resource = metric_data.metric.resource + for metric in data: + resource = metric.resource instrumentation_library_map = ( sdk_resource_instrumentation_library_metrics.get(resource, {}) ) @@ -95,26 +93,26 @@ def _translate_data( ] = instrumentation_library_map instrumentation_library_metrics = instrumentation_library_map.get( - metric_data.instrumentation_info + metric.instrumentation_info ) if not instrumentation_library_metrics: - if metric_data.instrumentation_info is not None: + if metric.instrumentation_info is not None: instrumentation_library_map[ - metric_data.instrumentation_info + metric.instrumentation_info ] = InstrumentationLibraryMetrics( instrumentation_library=InstrumentationLibrary( - name=metric_data.instrumentation_info.name, - version=metric_data.instrumentation_info.version, + name=metric.instrumentation_info.name, + version=metric.instrumentation_info.version, ) ) else: instrumentation_library_map[ - metric_data.instrumentation_info + metric.instrumentation_info ] = InstrumentationLibraryMetrics() instrumentation_library_metrics = instrumentation_library_map.get( - metric_data.instrumentation_info + metric.instrumentation_info ) instrumentation_library_metrics.metrics.append( @@ -128,7 +126,7 @@ def _translate_data( ) ) - def export(self, metrics: Sequence[MetricData]) -> MetricExportResult: + def export(self, metrics: Sequence[Metric]) -> MetricExportResult: return self._export(metrics) def shutdown(self): 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 index 54253c91415..82da7a0e98c 100644 --- 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 @@ -30,8 +30,12 @@ MetricsServiceServicer, add_MetricsServiceServicer_to_server, ) -from opentelemetry.sdk._metrics.data import Metric, MetricData from opentelemetry.sdk._metrics.export import MetricExportResult +from opentelemetry.sdk._metrics.point import ( + AggregationTemporality, + Metric, + Sum, +) from opentelemetry.sdk.environment_variables import ( OTEL_EXPORTER_OTLP_METRICS_INSECURE, ) @@ -96,13 +100,22 @@ def setUp(self): self.server.start() - self.metric_data_1 = MetricData( - metric=Metric( - resource=SDKResource({"key": "value"}), - ), + self.metric_data_1 = Metric( + resource=SDKResource({"key": "value"}), instrumentation_info=InstrumentationInfo( "first_name", "first_version" ), + attributes={}, + description="foo", + name="foometric", + unit="s", + point=Sum( + aggregation_temporality=AggregationTemporality.CUMULATIVE, + is_monotonic=True, + start_time_unix_nano=1641946015139533244, + time_unix_nano=1641946016139533244, + value=33, + ), ) def tearDown(self): diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py index 03a2debfe0b..3402f1a4747 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py @@ -14,23 +14,21 @@ from abc import ABC, abstractmethod from collections import OrderedDict -from enum import IntEnum from logging import getLogger from math import inf from threading import Lock from typing import Generic, Optional, Sequence, TypeVar from opentelemetry.sdk._metrics.measurement import Measurement -from opentelemetry.sdk._metrics.point import Gauge, Histogram, PointT, Sum +from opentelemetry.sdk._metrics.point import ( + AggregationTemporality, + Gauge, + Histogram, + PointT, + Sum, +) from opentelemetry.util._time import _time_ns - -class AggregationTemporality(IntEnum): - UNSPECIFIED = 0 - DELTA = 1 - CUMULATIVE = 2 - - _PointVarT = TypeVar("_PointVarT", bound=PointT) _logger = getLogger(__name__) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/data.py b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/data.py deleted file mode 100644 index e582d666841..00000000000 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/data.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright The OpenTelemetry Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from opentelemetry.sdk.resources import Resource -from opentelemetry.sdk.util.instrumentation import InstrumentationInfo - - -class Metric: - """TODO fill this in""" - - def __init__(self, resource: Resource) -> None: - self.resource = resource - - -class MetricData: - """Readable Metric data plus associated InstrumentationLibrary.""" - - def __init__( - self, - metric: Metric, - instrumentation_info: InstrumentationInfo, - ): - self.metric = metric - self.instrumentation_info = instrumentation_info diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/export/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/export/__init__.py index e9a83861fe9..b5e2f086104 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/export/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/export/__init__.py @@ -18,7 +18,7 @@ from sys import stdout from typing import IO, Callable, Sequence -from opentelemetry.sdk._metrics.data import Metric, MetricData +from opentelemetry.sdk._metrics.point import Metric class MetricExportResult(Enum): @@ -33,7 +33,8 @@ class MetricExporter(ABC): in their own format. """ - def export(self, metrics: Sequence[MetricData]) -> "MetricExportResult": + @abstractmethod + def export(self, metrics: Sequence[Metric]) -> "MetricExportResult": """Exports a batch of telemetry data. Args: @@ -68,9 +69,9 @@ def __init__( self.out = out self.formatter = formatter - def export(self, metrics: Sequence[MetricData]) -> MetricExportResult: - for data in metrics: - self.out.write(self.formatter(data.metric)) + def export(self, metrics: Sequence[Metric]) -> MetricExportResult: + for metric in metrics: + self.out.write(self.formatter(metric)) self.out.flush() return MetricExportResult.SUCCESS diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/point.py b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/point.py index 2503bb43e3b..64d62c5ba73 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/point.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/point.py @@ -13,15 +13,26 @@ # limitations under the License. from dataclasses import dataclass +from enum import IntEnum from typing import Sequence, Union +from opentelemetry.sdk.resources import Resource +from opentelemetry.sdk.util.instrumentation import InstrumentationInfo +from opentelemetry.util.types import Attributes + + +class AggregationTemporality(IntEnum): + UNSPECIFIED = 0 + DELTA = 1 + CUMULATIVE = 2 + @dataclass(frozen=True) class Sum: start_time_unix_nano: int time_unix_nano: int value: Union[int, float] - aggregation_temporality: int + aggregation_temporality: AggregationTemporality is_monotonic: bool @@ -37,7 +48,30 @@ class Histogram: time_unix_nano: int bucket_counts: Sequence[int] explicit_bounds: Sequence[float] - aggregation_temporality: int + aggregation_temporality: AggregationTemporality PointT = Union[Sum, Gauge, Histogram] + + +@dataclass(frozen=True) +class Metric: + """Represents a metric point in the OpenTelemetry data model to be exported + + Concrete metric types contain all the information as in the OTLP proto definitions + (https://tinyurl.com/7h6yx24v) but are flattened as much as possible. + """ + + # common fields to all metric kinds + attributes: Attributes + description: str + instrumentation_info: InstrumentationInfo + name: str + resource: Resource + unit: str + + point: PointT + """Contains non-common fields for the given metric""" + + def to_json(self) -> str: + raise NotImplementedError()