In the Python stdlib's logging
library, using the %(thread)
or %(threadName)
macro (documented here) calls threading.current_thread
(code), which takes out a threading
-internal lock.
This, as per the warnings in the __del__
documentation, causes a problem if you want to log inside __del__
. We've run into a single-threaded deadlocking issue caused by this.
So, my question: is there an always-safe way to log from inside __del__
methods? It'd be nice to log information about, in our case, connections being closed as the result of their owners being GC'd, but this can cause problems in the (perhaps unlikely) case that a user turns on debug logging and adds threading information to their debug configuration. I have found some posts about more explicit resource-sharing in __del__
, but none about the stdlib logging behavior.
The CPython stdlib version of
threading.current_thread()
doesn't actually take a lock. The issue you ran into is particular to eventlet, which does a bunch of monkey-patching to mess with the threading system. While one approach could be to stop using eventlet, that'd probably require you to rewrite your whole application, and it doesn't fix any of the other ways you might end up trying to take a lock while logging - for example, if a__str__
method turns out to need a lock.The closest thing to a safe way to log within
__del__
, or to do any complex work in__del__
, is probably to instead have__del__
send a message telling some other code to do the logging instead. This would introduce a delay between__del__
and the actual logging, but such a delay is essentially unavoidable, because we must delay the logging until the resources it needs are available. It would also not guarantee that the logging call and the__del__
happen on the same thread; in a non-eventlet context, it's probably safe to callcurrent_thread()
to figure out which thread is handling__del__
, but with eventlet, there probably isn't a good way.Most ways of sending the message would have similar thread-safety or reentrancy issues, but Python 3.7 adds the
queue.SimpleQueue
class with aput
method designed to be reentrant. Using it to manage your__del__
-logging messages might look something likeIn a non-eventlet context, it might be safe to have a
logging.QueueHandler
andlogging.QueueListener
handle the SimpleQueue, but with eventlet, that won't work, because we need to delay creation of the LogRecord until we're out of__del__
.