Skip to content
Open
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/5727.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`opentelemetry-api`: log the ``BoundedAttributes`` drop warning only once per record
11 changes: 8 additions & 3 deletions opentelemetry-api/src/opentelemetry/attributes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ def __init__(
self.dropped = 0
self.max_value_len = max_value_len
self._lock = threading.Lock()
# The spec allows the limit log to be emitted at most once per record, so track
# it separately from ``dropped``, which also counts other kinds of drops.
self._dropped_logged = False
# setting False before adding items
self._immutable = False
if attributes:
Expand Down Expand Up @@ -203,9 +206,11 @@ def _setitem_locked(self, key: str, value: types.AnyValue) -> None:
if key in self._dict:
del self._dict[key]
if self.maxlen is not None and len(self._dict) >= self.maxlen:
_logger.warning(
"Attributes dict is full. Dropping the oldest key-value pair from attributes to make space for the new key-value pair.",
)
if not self._dropped_logged:
self._dropped_logged = True
_logger.warning(
"Attributes dict is full. Dropping the oldest key-value pair from attributes to make space for the new key-value pair.",
)
# Dictionaries are insertion ordered in Python, this is the recommended way to get the oldest value.
del self._dict[next(iter(self._dict.keys()))]
self.dropped += 1
Expand Down
23 changes: 23 additions & 0 deletions opentelemetry-api/tests/attributes/test_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,29 @@ def test_maxlen_reached(self):
self.assertEqual(bdict["second"], "another")
self.assertEqual(bdict.dropped, 1)

def test_maxlen_reached_logs_warning_once(self):
bdict = BoundedAttributes(2, immutable=False)
with self.assertLogs("opentelemetry", level="WARNING") as cm:
for idx in range(5):
bdict[f"key-{idx}"] = idx

full_warnings = [warning for warning in cm.output if "Attributes dict is full" in warning]
self.assertEqual(len(full_warnings), 1)
self.assertEqual(len(bdict), 2)
self.assertEqual(bdict.dropped, 3)

def test_maxlen_reached_logs_warning_after_invalid_key_drop(self):
bdict = BoundedAttributes(2, immutable=False)
with self.assertLogs("opentelemetry", level="WARNING") as cm:
bdict[1] = 2
bdict["first"] = 1
bdict["second"] = 2
bdict["third"] = 3

full_warnings = [warning for warning in cm.output if "Attributes dict is full" in warning]
self.assertEqual(len(full_warnings), 1)
self.assertEqual(bdict.dropped, 2)

def test_negative_maxlen_not_allowed(self):
with self.assertRaises(ValueError):
BoundedAttributes(-1)
Expand Down
Loading