diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py index c1389594a38..c162ea5c1ca 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py @@ -20,7 +20,7 @@ import threading from contextlib import contextmanager from types import TracebackType -from typing import Iterator, Optional, Sequence, Tuple, Type +from typing import Iterator, MutableSequence, Optional, Sequence, Tuple, Type from opentelemetry import context as context_api from opentelemetry import trace as trace_api @@ -314,6 +314,9 @@ def set_attribute(self, key: str, value: types.AttributeValue) -> None: if error_message is not None: logger.warning("%s in attribute value sequence", error_message) return + # Freeze mutable sequences defensively + if isinstance(value, MutableSequence): + value = tuple(value) elif not isinstance(value, (bool, str, int, float)): logger.warning("invalid type for attribute value") return diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py index 4781c7cf805..ccb1ccd1e4e 100644 --- a/opentelemetry-sdk/tests/trace/test_trace.py +++ b/opentelemetry-sdk/tests/trace/test_trace.py @@ -412,8 +412,10 @@ def test_attributes(self): root.set_attribute("attr-key", "attr-value2") root.set_attribute("empty-list", []) - root.set_attribute("list-of-bools", [True, True, False]) - root.set_attribute("list-of-numerics", [123, 314, 0]) + list_of_bools = [True, True, False] + root.set_attribute("list-of-bools", list_of_bools) + list_of_numerics = [123, 314, 0] + root.set_attribute("list-of-numerics", list_of_numerics) self.assertEqual(len(root.attributes), 10) self.assertEqual(root.attributes["component"], "http") @@ -426,12 +428,20 @@ def test_attributes(self): self.assertEqual(root.attributes["http.status_text"], "OK") self.assertEqual(root.attributes["misc.pi"], 3.14) self.assertEqual(root.attributes["attr-key"], "attr-value2") - self.assertEqual(root.attributes["empty-list"], []) + self.assertEqual(root.attributes["empty-list"], ()) self.assertEqual( - root.attributes["list-of-bools"], [True, True, False] + root.attributes["list-of-bools"], (True, True, False) ) + list_of_bools.append(False) self.assertEqual( - root.attributes["list-of-numerics"], [123, 314, 0] + root.attributes["list-of-bools"], (True, True, False) + ) + self.assertEqual( + root.attributes["list-of-numerics"], (123, 314, 0) + ) + list_of_numerics.append(227) + self.assertEqual( + root.attributes["list-of-numerics"], (123, 314, 0) ) attributes = {