From 8378caff637c03bcd1c2d2978b586428a2c2ad55 Mon Sep 17 00:00:00 2001 From: Daniel Hochman Date: Fri, 5 Apr 2024 13:04:27 -0500 Subject: [PATCH 1/5] log the key when encoding the attribute fails --- .../otlp/proto/common/_internal/__init__.py | 3 +- .../tests/test_attribute_encoder.py | 96 +++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py index 6593d89fd87..324100ec037 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/src/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py @@ -109,7 +109,8 @@ def _encode_attributes( try: pb2_attributes.append(_encode_key_value(key, value)) except Exception as error: # pylint: disable=broad-except - _logger.exception(error) + _logger.exception("Failed to encode key %s: %s", key, error) + else: pb2_attributes = None return pb2_attributes diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py new file mode 100644 index 00000000000..8992651e59c --- /dev/null +++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py @@ -0,0 +1,96 @@ +import unittest +from unittest.mock import patch + +from opentelemetry.exporter.otlp.proto.common._internal import ( + _encode_attributes, +) +from opentelemetry.proto.common.v1.common_pb2 import AnyValue as PB2AnyValue +from opentelemetry.proto.common.v1.common_pb2 import ( + ArrayValue as PB2ArrayValue, +) +from opentelemetry.proto.common.v1.common_pb2 import KeyValue as PB2KeyValue + + +class TestOTLPAttributeEncoder(unittest.TestCase): + def test_encode_attributes_all_kinds(self): + result = _encode_attributes( + { + "a": 1, # int + "b": 3.14, # float + "c": False, # bool + "hello": "world", # str + "greet": ["hola", "bonjour"], # Sequence[str] + "data": [1, 2], # Sequence[int] + "data_granular": [1.4, 2.4], # Sequence[float] + } + ) + self.assertEqual( + result, + [ + PB2KeyValue(key="a", value=PB2AnyValue(int_value=1)), + PB2KeyValue(key="b", value=PB2AnyValue(double_value=3.14)), + PB2KeyValue(key="c", value=PB2AnyValue(bool_value=False)), + PB2KeyValue( + key="hello", value=PB2AnyValue(string_value="world") + ), + PB2KeyValue( + key="greet", + value=PB2AnyValue( + array_value=PB2ArrayValue( + values=[ + PB2AnyValue(string_value="hola"), + PB2AnyValue(string_value="bonjour"), + ] + ) + ), + ), + PB2KeyValue( + key="data", + value=PB2AnyValue( + array_value=PB2ArrayValue( + values=[ + PB2AnyValue(int_value=1), + PB2AnyValue(int_value=2), + ] + ) + ), + ), + PB2KeyValue( + key="data_granular", + value=PB2AnyValue( + array_value=PB2ArrayValue( + values=[ + PB2AnyValue(double_value=1.4), + PB2AnyValue(double_value=2.4), + ] + ) + ), + ), + ], + ) + + @patch( + "opentelemetry.exporter.otlp.proto.common._internal._logger.exception" + ) + def test_encode_attributes_error_logs_key(self, mock_logger_exception): + result = _encode_attributes({"a": 1, "bad_key": None, "b": 2}) + mock_logger_exception.assert_called_once() + self.assertEqual( + mock_logger_exception.call_args_list[0].args[0], + "Failed to encode key %s: %s", + ) + self.assertEqual( + mock_logger_exception.call_args_list[0].args[1], "bad_key" + ) + self.assertTrue( + isinstance( + mock_logger_exception.call_args_list[0].args[2], Exception + ) + ) + self.assertEqual( + result, + [ + PB2KeyValue(key="a", value=PB2AnyValue(int_value=1)), + PB2KeyValue(key="b", value=PB2AnyValue(int_value=2)), + ], + ) From 8ed207c816fffca7d46a03ffa365a561cd0c63a5 Mon Sep 17 00:00:00 2001 From: Daniel Hochman Date: Fri, 21 Jun 2024 15:56:24 -0500 Subject: [PATCH 2/5] use assertLogs --- .../tests/test_attribute_encoder.py | 28 ++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py index 8992651e59c..4f9297cc2e0 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py @@ -1,5 +1,5 @@ import unittest -from unittest.mock import patch +from logging import ERROR from opentelemetry.exporter.otlp.proto.common._internal import ( _encode_attributes, @@ -69,24 +69,14 @@ def test_encode_attributes_all_kinds(self): ], ) - @patch( - "opentelemetry.exporter.otlp.proto.common._internal._logger.exception" - ) - def test_encode_attributes_error_logs_key(self, mock_logger_exception): - result = _encode_attributes({"a": 1, "bad_key": None, "b": 2}) - mock_logger_exception.assert_called_once() - self.assertEqual( - mock_logger_exception.call_args_list[0].args[0], - "Failed to encode key %s: %s", - ) - self.assertEqual( - mock_logger_exception.call_args_list[0].args[1], "bad_key" - ) - self.assertTrue( - isinstance( - mock_logger_exception.call_args_list[0].args[2], Exception - ) - ) + def test_encode_attributes_error_logs_key(self): + with self.assertLogs(level=ERROR) as error: + result = _encode_attributes({"a": 1, "bad_key": None, "b": 2}) + + self.assertEqual(len(error.records), 1) + self.assertEqual(error.records[0].msg, f"Failed to encode key %s: %s") + self.assertEqual(error.records[0].args[0], "bad_key") + self.assertIsInstance(error.records[0].args[1], Exception) self.assertEqual( result, [ From b6f6b484d72ccad18b2480f73da34b97ec799cae Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Mon, 24 Jun 2024 10:51:39 +0200 Subject: [PATCH 3/5] Update exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Emídio Neto <9735060+emdneto@users.noreply.github.com> --- .../tests/test_attribute_encoder.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py index 4f9297cc2e0..ca620431352 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py @@ -1,3 +1,17 @@ +# 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. + import unittest from logging import ERROR From 1667b0aac092d7c5ac416c91f7be3655577d931b Mon Sep 17 00:00:00 2001 From: Daniel Hochman Date: Mon, 24 Jun 2024 14:45:27 -0500 Subject: [PATCH 4/5] fix test --- .../tests/test_attribute_encoder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py index ca620431352..01856611036 100644 --- a/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py +++ b/exporter/opentelemetry-exporter-otlp-proto-common/tests/test_attribute_encoder.py @@ -88,7 +88,7 @@ def test_encode_attributes_error_logs_key(self): result = _encode_attributes({"a": 1, "bad_key": None, "b": 2}) self.assertEqual(len(error.records), 1) - self.assertEqual(error.records[0].msg, f"Failed to encode key %s: %s") + self.assertEqual(error.records[0].msg, "Failed to encode key %s: %s") self.assertEqual(error.records[0].args[0], "bad_key") self.assertIsInstance(error.records[0].args[1], Exception) self.assertEqual( From 3a9d2c1df24ce7fe038a2f9e4183877505255e46 Mon Sep 17 00:00:00 2001 From: Daniel Hochman Date: Mon, 24 Jun 2024 14:48:03 -0500 Subject: [PATCH 5/5] CHANGELOG entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32698e94363..4c50093b9c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- When encountering an error encoding metric attributes in the OTLP exporter, log the key that had an error. + ([#3838](https://github.com/open-telemetry/opentelemetry-python/pull/3838)) - Log a warning when a `LogRecord` in `sdk/log` has dropped attributes due to reaching limits ([#3946](https://github.com/open-telemetry/opentelemetry-python/pull/3946))