Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Fix deadlock bug
  • Loading branch information
DylanRussell committed Jun 13, 2025
commit 87efdddf0c2e1390e81732d6ed8bcc817a84fa42
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ def __init__(
) -> None:
super().__init__(level=level)
self._logger_provider = logger_provider or get_logger_provider()
# self.flushOnClose = False
Comment thread
DylanRussell marked this conversation as resolved.
Outdated

@staticmethod
def _get_attributes(record: logging.LogRecord) -> _ExtendedAttributes:
Expand Down Expand Up @@ -584,7 +585,8 @@ def flush(self) -> None:
if hasattr(self._logger_provider, "force_flush") and callable(
self._logger_provider.force_flush
):
self._logger_provider.force_flush()
thread = threading.Thread(target=self._logger_provider.force_flush)
Comment thread
DylanRussell marked this conversation as resolved.
thread.start()


class Logger(APILogger):
Expand Down
28 changes: 28 additions & 0 deletions opentelemetry-sdk/tests/logs/test_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,34 @@ def test_handler_custom_log_level(self):
logger.critical("No Time For Caution")
self.assertEqual(processor.emit_count(), 2)

def test_handler_calling_flush_does_not_cause_deadlock(self):
class LogProcessorThatAccessesLockOnFlush(LogRecordProcessor):
def emit(self, log_data: LogData):
pass

def shutdown(self):
pass

def force_flush(self, timeout_millis: int = 30000):
# Deadlock will happen here IF `flush` starts a new thread
# and then blocks, if it just starts a thread and then returns
# we don't seem to encounter the issue..
logging._lock.acquire()
# assert logging._lock.acquire(False) is True
logging._lock.release()

logger_provider = LoggerProvider()
processor = LogProcessorThatAccessesLockOnFlush()
logger_provider.add_log_record_processor(processor)
handler = LoggingHandler(logger_provider=logger_provider)
logging.getLogger().addHandler(handler)
# The below code is essentially recreating what is causing the problem inside
# logging.config.dictConfig. Actually calling logging.config.dictConfig will modify
# global state inside the logging module and break lots of tests.
with logging._lock:
for handler in logging.getLogger().handlers:
handler.flush()

# pylint: disable=protected-access
def test_log_record_emit_noop(self):
noop_logger_provder = NoOpLoggerProvider()
Expand Down
Loading