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
19 changes: 12 additions & 7 deletions logtail/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,28 @@ def _parse_custom_events(record, include_extra_attributes):
def _remove_circular_dependencies(obj, memo=None):
if memo is None:
memo = set()

# Skip immutable types, which can't contain circular dependencies
if isinstance(obj, (str, int, float, bool)):
if isinstance(obj, (str, int, float, bool, type(None))):
return obj
if id(obj) in memo:

# For mutable objects, check for circular references
obj_id = id(obj)
if obj_id in memo:
return "<omitted circular reference>"
memo.add(id(obj))
memo.add(obj_id)

if isinstance(obj, dict):
new_dict = {}
for key, value in obj.items():
new_dict[key] = _remove_circular_dependencies(value, memo)
new_dict[key] = _remove_circular_dependencies(value, memo.copy())
return new_dict
elif isinstance(obj, list):
return [_remove_circular_dependencies(item, memo) for item in obj]
return [_remove_circular_dependencies(item, memo.copy()) for item in obj]
elif isinstance(obj, tuple):
return tuple(_remove_circular_dependencies(item, memo) for item in obj)
return tuple(_remove_circular_dependencies(item, memo.copy()) for item in obj)
elif isinstance(obj, set):
return {_remove_circular_dependencies(item, memo) for item in obj}
return {_remove_circular_dependencies(item, memo.copy()) for item in obj}
else:
return obj

Expand Down
21 changes: 21 additions & 0 deletions tests/test_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,27 @@ def test_can_have_multiple_instance_of_same_string_in_extra_data(self, MockWorke
self.assertEqual(log_entry['test2'], 'this is a test string')
self.assertTrue(handler.pipe.empty())

@patch('logtail.handler.FlushWorker')
def test_can_have_multiple_instance_of_same_array_in_extra_data(self, MockWorker):
buffer_capacity = 1
handler = LogtailHandler(
source_token=self.source_token,
buffer_capacity=buffer_capacity
)

logger = logging.getLogger(__name__)
logger.handlers = []
logger.addHandler(handler)
test_array = ['this is a test string']
logger.info('hello', extra={'test1': test_array, 'test2': test_array})

log_entry = handler.pipe.get()

self.assertEqual(log_entry['message'], 'hello')
self.assertEqual(log_entry['test1'], ['this is a test string'])
self.assertEqual(log_entry['test2'], ['this is a test string'])
self.assertTrue(handler.pipe.empty())

@patch('logtail.handler.FlushWorker')
def test_can_send_circular_dependency_in_context(self, MockWorker):
buffer_capacity = 1
Expand Down