Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changelog/5275.changed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`opentelemetry-sdk`: inline the method `_clean_attribute_value`
44 changes: 22 additions & 22 deletions opentelemetry-api/src/opentelemetry/attributes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ def _type_name(t):
_logger = logging.getLogger(__name__)


# pylint: disable=too-many-return-statements
# pylint: disable=too-many-branches
def _clean_attribute(
key: str, value: types.AttributeValue, max_len: int | None
) -> types.AttributeValue | tuple[str | int | float, ...] | None:
Expand All @@ -58,16 +60,32 @@ def _clean_attribute(
return None

if isinstance(value, _VALID_ATTR_VALUE_TYPES):
return _clean_attribute_value(value, max_len)
if isinstance(value, bytes):
try:
value = value.decode()
except UnicodeDecodeError:
_logger.warning("Byte attribute could not be decoded.")
return None
if max_len is not None and isinstance(value, str):
value = value[:max_len]
return value

if isinstance(value, Sequence):
sequence_first_valid_type = None
cleaned_seq = []

for element in value:
element = _clean_attribute_value(element, max_len) # type: ignore
if element is None:
cleaned_seq.append(element)
if isinstance(element, bytes):
try:
element = element.decode()
except UnicodeDecodeError:
_logger.warning("Byte attribute could not be decoded.")
cleaned_seq.append(None)
continue
if max_len is not None and isinstance(element, str):
element = element[:max_len]
elif element is None:
cleaned_seq.append(None)
continue

element_type = type(element)
Expand Down Expand Up @@ -214,24 +232,6 @@ def _clean_extended_attribute(
return None


def _clean_attribute_value(
value: types.AttributeValue, limit: int | None
) -> types.AttributeValue | None:
if value is None:
return None

if isinstance(value, bytes):
try:
value = value.decode()
except UnicodeDecodeError:
_logger.warning("Byte attribute could not be decoded.")
return None

if limit is not None and isinstance(value, str):
value = value[:limit]
return value


class BoundedAttributes(MutableMapping): # type: ignore
"""An ordered dict with a fixed max capacity.

Expand Down
28 changes: 28 additions & 0 deletions opentelemetry-sdk/benchmarks/trace/test_benchmark_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,34 @@ def benchmark_set_attribute():
benchmark(benchmark_set_attribute)


@pytest.mark.parametrize(
"attr_type,value",
[
("bool", True),
("int", 42),
("float", 3.14),
("str", "hello world"),
("bytes", b"hello world"),
("seq_bool", (True, False, True)),
("seq_int", (1, 2, 3, 4, 5)),
("seq_float", (1.1, 2.2, 3.3)),
("seq_str", ("a", "b", "c", "d", "e")),
("seq_bytes", (b"a", b"b", b"c")),
],
)
def test_set_attribute_types(benchmark, attr_type, value):
attrs = {f"key{i}": value for i in range(128)}

def benchmark_set_attribute():
for _ in range(5_000):
span = tracer.start_span("benchmarkedSpan")
for key, val in attrs.items():
span.set_attribute(key, val)
span.end()

benchmark(benchmark_set_attribute)


def test_simple_start_as_current_span(benchmark):
def benchmark_start_as_current_span():
with tracer.start_as_current_span(
Expand Down
Loading