From a3b4d8eb7f1339068745d4a0a8625d9f3b0fd4b2 Mon Sep 17 00:00:00 2001 From: yiyuanh Date: Thu, 27 Feb 2025 13:51:11 -0800 Subject: [PATCH 1/6] contribute otlp udp exporter --- .../aws-otel-otlp-udp-exporter/README.rst | 17 ++ .../aws-otel-otlp-udp-exporter/pyproject.toml | 36 +++++ .../src/__init__.py | 0 .../src/amazon/__init__.py | 0 .../src/amazon/opentelemetry/__init__.py | 0 .../opentelemetry/exporters/__init__.py | 0 .../opentelemetry/exporters/otlp/__init__.py | 0 .../exporters/otlp/udp/__init__.py | 24 +++ .../exporters/otlp/udp/exporter.py | 136 ++++++++++++++++ .../tests/test_exporter.py | 146 ++++++++++++++++++ 10 files changed, 359 insertions(+) create mode 100644 exporters/aws-otel-otlp-udp-exporter/README.rst create mode 100644 exporters/aws-otel-otlp-udp-exporter/pyproject.toml create mode 100644 exporters/aws-otel-otlp-udp-exporter/src/__init__.py create mode 100644 exporters/aws-otel-otlp-udp-exporter/src/amazon/__init__.py create mode 100644 exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/__init__.py create mode 100644 exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/__init__.py create mode 100644 exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/__init__.py create mode 100644 exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/__init__.py create mode 100644 exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/exporter.py create mode 100644 exporters/aws-otel-otlp-udp-exporter/tests/test_exporter.py diff --git a/exporters/aws-otel-otlp-udp-exporter/README.rst b/exporters/aws-otel-otlp-udp-exporter/README.rst new file mode 100644 index 000000000..6d5e3d723 --- /dev/null +++ b/exporters/aws-otel-otlp-udp-exporter/README.rst @@ -0,0 +1,17 @@ +AWS OpenTelemetry OTLP UDP Exporter +=================================== + +Installation +------------ + +:: + + pip install aws-otel-otlp-udp-exporter + + +This package provides a UDP exporter for OpenTelemetry. + +References +---------- + +* `OpenTelemetry Project `_ diff --git a/exporters/aws-otel-otlp-udp-exporter/pyproject.toml b/exporters/aws-otel-otlp-udp-exporter/pyproject.toml new file mode 100644 index 000000000..aa27c5ecd --- /dev/null +++ b/exporters/aws-otel-otlp-udp-exporter/pyproject.toml @@ -0,0 +1,36 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "aws-otel-otlp-udp-exporter" +version = "0.1.0" +description = "OTLP UDP Exporter for OpenTelemetry" +readme = "README.rst" +license = "Apache-2.0" +requires-python = ">=3.8" +authors = [ + { name = "Amazon Web Services" } +] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] + +dependencies = [ + "opentelemetry-sdk == 1.27.0", + "opentelemetry-exporter-otlp-proto-grpc == 1.27.0", +] + +[project.urls] +Homepage = "https://github.com/aws-observability/aws-otel-python-instrumentation/tree/main/exporters" + +[tool.hatch.build.targets.wheel] +packages = ["src/amazon"] diff --git a/exporters/aws-otel-otlp-udp-exporter/src/__init__.py b/exporters/aws-otel-otlp-udp-exporter/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/exporters/aws-otel-otlp-udp-exporter/src/amazon/__init__.py b/exporters/aws-otel-otlp-udp-exporter/src/amazon/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/__init__.py b/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/__init__.py b/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/__init__.py b/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/__init__.py b/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/__init__.py new file mode 100644 index 000000000..f7cafb4eb --- /dev/null +++ b/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/__init__.py @@ -0,0 +1,24 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +from .exporter import ( + DEFAULT_ENDPOINT, + FORMAT_OTEL_METRICS_BINARY_PREFIX, + FORMAT_OTEL_SAMPLED_TRACES_BINARY_PREFIX, + FORMAT_OTEL_UNSAMPLED_TRACES_BINARY_PREFIX, + PROTOCOL_HEADER, + OTLPUdpMetricExporter, + OTLPUdpSpanExporter, + UdpExporter, +) + +__all__ = [ + "UdpExporter", + "OTLPUdpMetricExporter", + "OTLPUdpSpanExporter", + "DEFAULT_ENDPOINT", + "FORMAT_OTEL_SAMPLED_TRACES_BINARY_PREFIX", + "FORMAT_OTEL_UNSAMPLED_TRACES_BINARY_PREFIX", + "PROTOCOL_HEADER", + "FORMAT_OTEL_METRICS_BINARY_PREFIX", +] diff --git a/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/exporter.py b/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/exporter.py new file mode 100644 index 000000000..9466837ec --- /dev/null +++ b/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/exporter.py @@ -0,0 +1,136 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +import base64 +import os +import socket +from logging import Logger, getLogger +from typing import Dict, Optional, Sequence, Tuple + +from typing_extensions import override + +from opentelemetry.exporter.otlp.proto.common.metrics_encoder import encode_metrics +from opentelemetry.exporter.otlp.proto.common.trace_encoder import encode_spans +from opentelemetry.sdk.metrics._internal.aggregation import AggregationTemporality +from opentelemetry.sdk.metrics._internal.export import MetricExportResult +from opentelemetry.sdk.metrics._internal.point import MetricsData +from opentelemetry.sdk.metrics.export import MetricExporter +from opentelemetry.sdk.metrics.view import Aggregation +from opentelemetry.sdk.trace import ReadableSpan +from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult + +DEFAULT_ENDPOINT = "127.0.0.1:2000" +PROTOCOL_HEADER = '{"format":"json","version":1}\n' +FORMAT_OTEL_METRICS_BINARY_PREFIX = "M1" + +FORMAT_OTEL_SAMPLED_TRACES_BINARY_PREFIX = "T1S" +FORMAT_OTEL_UNSAMPLED_TRACES_BINARY_PREFIX = "T1U" + +_logger: Logger = getLogger(__name__) + + +class UdpExporter: + def __init__(self, endpoint: Optional[str] = None): + if endpoint is None and "AWS_LAMBDA_FUNCTION_NAME" in os.environ: + # If in an AWS Lambda Environment, `AWS_XRAY_DAEMON_ADDRESS` will be defined + endpoint = os.environ.get("AWS_XRAY_DAEMON_ADDRESS", DEFAULT_ENDPOINT) + + self._endpoint = endpoint or DEFAULT_ENDPOINT + self._host, self._port = self._parse_endpoint(self._endpoint) + self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self._socket.setblocking(False) + + def send_data(self, data: bytes, signal_format_prefix: str): + # base64 encoding and then converting to string with utf-8 + base64_encoded_string: str = base64.b64encode(data).decode("utf-8") + message = f"{PROTOCOL_HEADER}{signal_format_prefix}{base64_encoded_string}" + + try: + _logger.debug("Sending UDP data: %s", message) + self._socket.sendto(message.encode("utf-8"), (self._host, int(self._port))) + except Exception as exc: # pylint: disable=broad-except + _logger.error("Error sending UDP data: %s", exc) + raise + + def shutdown(self): + self._socket.close() + + # pylint: disable=no-self-use + def _parse_endpoint(self, endpoint: str) -> Tuple[str, int]: + try: + vals = endpoint.split(":") + host = vals[0] + port = int(vals[1]) + except Exception as exc: # pylint: disable=broad-except + raise ValueError(f"Invalid endpoint: {endpoint}") from exc + + return host, port + + +class OTLPUdpMetricExporter(MetricExporter): + def __init__( + self, + endpoint: Optional[str] = None, + preferred_temporality: Dict[type, AggregationTemporality] = None, + preferred_aggregation: Dict[type, Aggregation] = None, + ): + super().__init__( + preferred_temporality=preferred_temporality, + preferred_aggregation=preferred_aggregation, + ) + self._udp_exporter = UdpExporter(endpoint=endpoint) + + @override + def export( + self, + metrics_data: MetricsData, + timeout_millis: float = 10_000, + **kwargs, + ) -> MetricExportResult: + serialized_data = encode_metrics(metrics_data).SerializeToString() + + try: + self._udp_exporter.send_data(data=serialized_data, signal_format_prefix=FORMAT_OTEL_METRICS_BINARY_PREFIX) + return MetricExportResult.SUCCESS + except Exception as exc: # pylint: disable=broad-except + _logger.error("Error exporting metrics: %s", exc) + return MetricExportResult.FAILURE + + # pylint: disable=no-self-use + def force_flush(self, timeout_millis: float = 10_000) -> bool: + # TODO: implement force flush + return True + + def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None: + self._udp_exporter.shutdown() + + +class OTLPUdpSpanExporter(SpanExporter): + def __init__(self, endpoint: Optional[str] = None, sampled: bool = True): + self._udp_exporter = UdpExporter(endpoint=endpoint) + self._sampled = sampled + + @override + def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: + serialized_data = encode_spans(spans).SerializeToString() + + try: + prefix = ( + FORMAT_OTEL_SAMPLED_TRACES_BINARY_PREFIX + if self._sampled + else FORMAT_OTEL_UNSAMPLED_TRACES_BINARY_PREFIX + ) + self._udp_exporter.send_data(data=serialized_data, signal_format_prefix=prefix) + return SpanExportResult.SUCCESS + except Exception as exc: # pylint: disable=broad-except + _logger.error("Error exporting spans: %s", exc) + return SpanExportResult.FAILURE + + # pylint: disable=no-self-use + @override + def force_flush(self, timeout_millis: int = 30000) -> bool: + # TODO: implement force flush + return True + + @override + def shutdown(self) -> None: + self._udp_exporter.shutdown() diff --git a/exporters/aws-otel-otlp-udp-exporter/tests/test_exporter.py b/exporters/aws-otel-otlp-udp-exporter/tests/test_exporter.py new file mode 100644 index 000000000..c2896ac47 --- /dev/null +++ b/exporters/aws-otel-otlp-udp-exporter/tests/test_exporter.py @@ -0,0 +1,146 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +import base64 +import socket +import unittest +from unittest import TestCase +from unittest.mock import MagicMock, patch + +from amazon.opentelemetry.exporters.otlp.udp import ( + DEFAULT_ENDPOINT, + FORMAT_OTEL_SAMPLED_TRACES_BINARY_PREFIX, + FORMAT_OTEL_UNSAMPLED_TRACES_BINARY_PREFIX, + PROTOCOL_HEADER, + OTLPUdpMetricExporter, + OTLPUdpSpanExporter, + UdpExporter, +) +from opentelemetry.sdk.metrics._internal.export import MetricExportResult +from opentelemetry.sdk.trace.export import SpanExportResult + + +class TestUdpExporter(TestCase): + + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.socket.socket") + def test_udp_exporter_init_default(self, mock_socket): + exporter = UdpExporter() + self.assertEqual(exporter._endpoint, DEFAULT_ENDPOINT) + self.assertEqual(exporter._host, "127.0.0.1") + self.assertEqual(exporter._port, 2000) + mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_DGRAM) + mock_socket().setblocking.assert_called_once_with(False) + + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.socket.socket") + def test_udp_exporter_init_with_endpoint(self, mock_socket): + exporter = UdpExporter(endpoint="localhost:5000") + self.assertNotEqual(exporter._endpoint, DEFAULT_ENDPOINT) + self.assertEqual(exporter._host, "localhost") + self.assertEqual(exporter._port, 5000) + mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_DGRAM) + mock_socket().setblocking.assert_called_once_with(False) + + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.socket.socket") + def test_udp_exporter_init_invalid_endpoint(self, mock_socket): + with self.assertRaises(ValueError): + UdpExporter(endpoint="invalidEndpoint:port") + + # pylint: disable=no-self-use + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.socket.socket") + def test_send_data(self, mock_socket): + mock_socket_instance = mock_socket.return_value + exporter = UdpExporter() + input_bytes: bytes = b"hello" + encoded_bytes: bytes = base64.b64encode(input_bytes) + exporter.send_data(input_bytes, "signal_prefix") + expected_message = PROTOCOL_HEADER + "signal_prefix" + encoded_bytes.decode("utf-8") + mock_socket_instance.sendto.assert_called_once_with(expected_message.encode("utf-8"), ("127.0.0.1", 2000)) + + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.socket.socket") + def test_shutdown(self, mock_socket): + mock_socket_instance = mock_socket.return_value + exporter = UdpExporter() + exporter.shutdown() + mock_socket_instance.close.assert_called_once() + + +class TestOTLPUdpMetricExporter(unittest.TestCase): + + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.encode_metrics") + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.UdpExporter") + def test_export(self, mock_udp_exporter, mock_encode_metrics): + mock_udp_exporter_instance = mock_udp_exporter.return_value + mock_encoded_data = MagicMock() + mock_encode_metrics.return_value.SerializeToString.return_value = mock_encoded_data + exporter = OTLPUdpMetricExporter() + result = exporter.export(MagicMock()) + mock_udp_exporter_instance.send_data.assert_called_once_with(data=mock_encoded_data, signal_format_prefix="M1") + self.assertEqual(result, MetricExportResult.SUCCESS) + + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.encode_metrics") + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.UdpExporter") + def test_export_with_exception(self, mock_udp_exporter, mock_encode_metrics): + mock_udp_exporter_instance = mock_udp_exporter.return_value + mock_encoded_data = MagicMock() + mock_encode_metrics.return_value.SerializeToString.return_value = mock_encoded_data + mock_udp_exporter_instance.send_data.side_effect = Exception("Something went wrong") + exporter = OTLPUdpMetricExporter() + result = exporter.export(MagicMock()) + self.assertEqual(result, MetricExportResult.FAILURE) + + # pylint: disable=no-self-use + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.UdpExporter") + def test_shutdown(self, mock_udp_exporter): + mock_udp_exporter_instance = mock_udp_exporter.return_value + exporter = OTLPUdpMetricExporter() + exporter.force_flush() + exporter.shutdown() + mock_udp_exporter_instance.shutdown.assert_called_once() + + +class TestOTLPUdpSpanExporter(unittest.TestCase): + + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.encode_spans") + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.UdpExporter") + def test_export_unsampled_span(self, mock_udp_exporter, mock_encode_spans): + mock_udp_exporter_instance = mock_udp_exporter.return_value + mock_encoded_data = MagicMock() + mock_encode_spans.return_value.SerializeToString.return_value = mock_encoded_data + exporter = OTLPUdpSpanExporter(sampled=False) + result = exporter.export(MagicMock()) + mock_udp_exporter_instance.send_data.assert_called_once_with( + data=mock_encoded_data, signal_format_prefix=FORMAT_OTEL_UNSAMPLED_TRACES_BINARY_PREFIX + ) + self.assertEqual(result, SpanExportResult.SUCCESS) + + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.encode_spans") + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.UdpExporter") + def test_export_sampled_span(self, mock_udp_exporter, mock_encode_spans): + mock_udp_exporter_instance = mock_udp_exporter.return_value + mock_encoded_data = MagicMock() + mock_encode_spans.return_value.SerializeToString.return_value = mock_encoded_data + exporter = OTLPUdpSpanExporter() + result = exporter.export(MagicMock()) + mock_udp_exporter_instance.send_data.assert_called_once_with( + data=mock_encoded_data, signal_format_prefix=FORMAT_OTEL_SAMPLED_TRACES_BINARY_PREFIX + ) + self.assertEqual(result, SpanExportResult.SUCCESS) + + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.encode_spans") + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.UdpExporter") + def test_export_with_exception(self, mock_udp_exporter, mock_encode_spans): + mock_udp_exporter_instance = mock_udp_exporter.return_value + mock_encoded_data = MagicMock() + mock_encode_spans.return_value.SerializeToString.return_value = mock_encoded_data + mock_udp_exporter_instance.send_data.side_effect = Exception("Something went wrong") + exporter = OTLPUdpSpanExporter() + result = exporter.export(MagicMock()) + self.assertEqual(result, SpanExportResult.FAILURE) + + # pylint: disable=no-self-use + @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.UdpExporter") + def test_shutdown(self, mock_udp_exporter): + mock_udp_exporter_instance = mock_udp_exporter.return_value + exporter = OTLPUdpSpanExporter() + exporter.shutdown() + exporter.force_flush() + mock_udp_exporter_instance.shutdown.assert_called_once() From 0903bb669fb6205b04769cf2bc6dc50de615cd21 Mon Sep 17 00:00:00 2001 From: yiyuanh Date: Fri, 28 Feb 2025 13:54:52 -0800 Subject: [PATCH 2/6] update udp exporter homepage link in pyproject.toml --- exporters/aws-otel-otlp-udp-exporter/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporters/aws-otel-otlp-udp-exporter/pyproject.toml b/exporters/aws-otel-otlp-udp-exporter/pyproject.toml index aa27c5ecd..abb9e81b5 100644 --- a/exporters/aws-otel-otlp-udp-exporter/pyproject.toml +++ b/exporters/aws-otel-otlp-udp-exporter/pyproject.toml @@ -30,7 +30,7 @@ dependencies = [ ] [project.urls] -Homepage = "https://github.com/aws-observability/aws-otel-python-instrumentation/tree/main/exporters" +Homepage = "https://github.com/aws-observability/aws-otel-python-instrumentation/tree/main/exporters/aws-otel-otlp-udp-exporter" [tool.hatch.build.targets.wheel] packages = ["src/amazon"] From b73cc9e0a9f45e12a1110666e4cf6f66b6c0a2dd Mon Sep 17 00:00:00 2001 From: yiyuanh Date: Fri, 28 Feb 2025 15:10:18 -0800 Subject: [PATCH 3/6] remove __init__.py files in subdirectories --- exporters/aws-otel-otlp-udp-exporter/src/__init__.py | 0 exporters/aws-otel-otlp-udp-exporter/src/amazon/__init__.py | 0 .../src/amazon/opentelemetry/__init__.py | 0 .../src/amazon/opentelemetry/exporters/__init__.py | 0 .../src/amazon/opentelemetry/exporters/otlp/__init__.py | 0 5 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 exporters/aws-otel-otlp-udp-exporter/src/__init__.py delete mode 100644 exporters/aws-otel-otlp-udp-exporter/src/amazon/__init__.py delete mode 100644 exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/__init__.py delete mode 100644 exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/__init__.py delete mode 100644 exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/__init__.py diff --git a/exporters/aws-otel-otlp-udp-exporter/src/__init__.py b/exporters/aws-otel-otlp-udp-exporter/src/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/exporters/aws-otel-otlp-udp-exporter/src/amazon/__init__.py b/exporters/aws-otel-otlp-udp-exporter/src/amazon/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/__init__.py b/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/__init__.py b/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/__init__.py b/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/__init__.py deleted file mode 100644 index e69de29bb..000000000 From e3e447c11077e0b42f65ad8932b625f74ee3d3d4 Mon Sep 17 00:00:00 2001 From: yiyuanh Date: Fri, 28 Feb 2025 16:41:13 -0800 Subject: [PATCH 4/6] update dependency in pyproject to use proto-http instead of proto-grpc --- exporters/aws-otel-otlp-udp-exporter/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporters/aws-otel-otlp-udp-exporter/pyproject.toml b/exporters/aws-otel-otlp-udp-exporter/pyproject.toml index abb9e81b5..220a15580 100644 --- a/exporters/aws-otel-otlp-udp-exporter/pyproject.toml +++ b/exporters/aws-otel-otlp-udp-exporter/pyproject.toml @@ -26,7 +26,7 @@ classifiers = [ dependencies = [ "opentelemetry-sdk == 1.27.0", - "opentelemetry-exporter-otlp-proto-grpc == 1.27.0", + "opentelemetry-exporter-otlp-proto-http == 1.27.0", ] [project.urls] From ef032f8a8db6d5404b6651aea21f91107d4492f7 Mon Sep 17 00:00:00 2001 From: yiyuanh Date: Fri, 28 Feb 2025 16:46:31 -0800 Subject: [PATCH 5/6] update proto dependency to proto-common --- exporters/aws-otel-otlp-udp-exporter/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporters/aws-otel-otlp-udp-exporter/pyproject.toml b/exporters/aws-otel-otlp-udp-exporter/pyproject.toml index 220a15580..bff54cbf7 100644 --- a/exporters/aws-otel-otlp-udp-exporter/pyproject.toml +++ b/exporters/aws-otel-otlp-udp-exporter/pyproject.toml @@ -26,7 +26,7 @@ classifiers = [ dependencies = [ "opentelemetry-sdk == 1.27.0", - "opentelemetry-exporter-otlp-proto-http == 1.27.0", + "opentelemetry-exporter-otlp-proto-common == 1.27.0", ] [project.urls] From 27a1bd481b1b55e3f429196256e09defa852c1fd Mon Sep 17 00:00:00 2001 From: yiyuanh Date: Mon, 3 Mar 2025 10:10:00 -0800 Subject: [PATCH 6/6] remove metrics exporter --- .../exporters/otlp/udp/__init__.py | 4 -- .../exporters/otlp/udp/exporter.py | 47 +------------------ .../tests/test_exporter.py | 36 -------------- 3 files changed, 1 insertion(+), 86 deletions(-) diff --git a/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/__init__.py b/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/__init__.py index f7cafb4eb..1872b41ec 100644 --- a/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/__init__.py +++ b/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/__init__.py @@ -3,22 +3,18 @@ from .exporter import ( DEFAULT_ENDPOINT, - FORMAT_OTEL_METRICS_BINARY_PREFIX, FORMAT_OTEL_SAMPLED_TRACES_BINARY_PREFIX, FORMAT_OTEL_UNSAMPLED_TRACES_BINARY_PREFIX, PROTOCOL_HEADER, - OTLPUdpMetricExporter, OTLPUdpSpanExporter, UdpExporter, ) __all__ = [ "UdpExporter", - "OTLPUdpMetricExporter", "OTLPUdpSpanExporter", "DEFAULT_ENDPOINT", "FORMAT_OTEL_SAMPLED_TRACES_BINARY_PREFIX", "FORMAT_OTEL_UNSAMPLED_TRACES_BINARY_PREFIX", "PROTOCOL_HEADER", - "FORMAT_OTEL_METRICS_BINARY_PREFIX", ] diff --git a/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/exporter.py b/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/exporter.py index 9466837ec..095d40904 100644 --- a/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/exporter.py +++ b/exporters/aws-otel-otlp-udp-exporter/src/amazon/opentelemetry/exporters/otlp/udp/exporter.py @@ -4,23 +4,16 @@ import os import socket from logging import Logger, getLogger -from typing import Dict, Optional, Sequence, Tuple +from typing import Optional, Sequence, Tuple from typing_extensions import override -from opentelemetry.exporter.otlp.proto.common.metrics_encoder import encode_metrics from opentelemetry.exporter.otlp.proto.common.trace_encoder import encode_spans -from opentelemetry.sdk.metrics._internal.aggregation import AggregationTemporality -from opentelemetry.sdk.metrics._internal.export import MetricExportResult -from opentelemetry.sdk.metrics._internal.point import MetricsData -from opentelemetry.sdk.metrics.export import MetricExporter -from opentelemetry.sdk.metrics.view import Aggregation from opentelemetry.sdk.trace import ReadableSpan from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult DEFAULT_ENDPOINT = "127.0.0.1:2000" PROTOCOL_HEADER = '{"format":"json","version":1}\n' -FORMAT_OTEL_METRICS_BINARY_PREFIX = "M1" FORMAT_OTEL_SAMPLED_TRACES_BINARY_PREFIX = "T1S" FORMAT_OTEL_UNSAMPLED_TRACES_BINARY_PREFIX = "T1U" @@ -66,44 +59,6 @@ def _parse_endpoint(self, endpoint: str) -> Tuple[str, int]: return host, port -class OTLPUdpMetricExporter(MetricExporter): - def __init__( - self, - endpoint: Optional[str] = None, - preferred_temporality: Dict[type, AggregationTemporality] = None, - preferred_aggregation: Dict[type, Aggregation] = None, - ): - super().__init__( - preferred_temporality=preferred_temporality, - preferred_aggregation=preferred_aggregation, - ) - self._udp_exporter = UdpExporter(endpoint=endpoint) - - @override - def export( - self, - metrics_data: MetricsData, - timeout_millis: float = 10_000, - **kwargs, - ) -> MetricExportResult: - serialized_data = encode_metrics(metrics_data).SerializeToString() - - try: - self._udp_exporter.send_data(data=serialized_data, signal_format_prefix=FORMAT_OTEL_METRICS_BINARY_PREFIX) - return MetricExportResult.SUCCESS - except Exception as exc: # pylint: disable=broad-except - _logger.error("Error exporting metrics: %s", exc) - return MetricExportResult.FAILURE - - # pylint: disable=no-self-use - def force_flush(self, timeout_millis: float = 10_000) -> bool: - # TODO: implement force flush - return True - - def shutdown(self, timeout_millis: float = 30_000, **kwargs) -> None: - self._udp_exporter.shutdown() - - class OTLPUdpSpanExporter(SpanExporter): def __init__(self, endpoint: Optional[str] = None, sampled: bool = True): self._udp_exporter = UdpExporter(endpoint=endpoint) diff --git a/exporters/aws-otel-otlp-udp-exporter/tests/test_exporter.py b/exporters/aws-otel-otlp-udp-exporter/tests/test_exporter.py index c2896ac47..15514e14a 100644 --- a/exporters/aws-otel-otlp-udp-exporter/tests/test_exporter.py +++ b/exporters/aws-otel-otlp-udp-exporter/tests/test_exporter.py @@ -11,11 +11,9 @@ FORMAT_OTEL_SAMPLED_TRACES_BINARY_PREFIX, FORMAT_OTEL_UNSAMPLED_TRACES_BINARY_PREFIX, PROTOCOL_HEADER, - OTLPUdpMetricExporter, OTLPUdpSpanExporter, UdpExporter, ) -from opentelemetry.sdk.metrics._internal.export import MetricExportResult from opentelemetry.sdk.trace.export import SpanExportResult @@ -63,40 +61,6 @@ def test_shutdown(self, mock_socket): mock_socket_instance.close.assert_called_once() -class TestOTLPUdpMetricExporter(unittest.TestCase): - - @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.encode_metrics") - @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.UdpExporter") - def test_export(self, mock_udp_exporter, mock_encode_metrics): - mock_udp_exporter_instance = mock_udp_exporter.return_value - mock_encoded_data = MagicMock() - mock_encode_metrics.return_value.SerializeToString.return_value = mock_encoded_data - exporter = OTLPUdpMetricExporter() - result = exporter.export(MagicMock()) - mock_udp_exporter_instance.send_data.assert_called_once_with(data=mock_encoded_data, signal_format_prefix="M1") - self.assertEqual(result, MetricExportResult.SUCCESS) - - @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.encode_metrics") - @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.UdpExporter") - def test_export_with_exception(self, mock_udp_exporter, mock_encode_metrics): - mock_udp_exporter_instance = mock_udp_exporter.return_value - mock_encoded_data = MagicMock() - mock_encode_metrics.return_value.SerializeToString.return_value = mock_encoded_data - mock_udp_exporter_instance.send_data.side_effect = Exception("Something went wrong") - exporter = OTLPUdpMetricExporter() - result = exporter.export(MagicMock()) - self.assertEqual(result, MetricExportResult.FAILURE) - - # pylint: disable=no-self-use - @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.UdpExporter") - def test_shutdown(self, mock_udp_exporter): - mock_udp_exporter_instance = mock_udp_exporter.return_value - exporter = OTLPUdpMetricExporter() - exporter.force_flush() - exporter.shutdown() - mock_udp_exporter_instance.shutdown.assert_called_once() - - class TestOTLPUdpSpanExporter(unittest.TestCase): @patch("amazon.opentelemetry.exporters.otlp.udp.exporter.encode_spans")