我可以在__del__方法中使用线程名称/ ID登录吗?

时间:2018-11-09 16:04:32

标签: python logging del

在Python stdlib的logging库中,使用%(thread)%(threadName)宏(documented here)调用threading.current_threadcode),取出threading内部锁。

如果要登录__del__,这as per the warnings in the __del__ documentation会引起问题。我们碰到了a single-threaded deadlocking issue

所以,我的问题是:是否有一种始终安全的方法来从__del__方法内部进行登录?在我们的情况下,最好记录有关由于所有者被GC关闭而关闭连接的信息,但是在用户打开调试日志记录(em)的情况下(可能不太可能),这可能会导致问题。并且将线程信息添加到其调试配置中。我发现了一些有关more explicit resource-sharing in __del__的帖子,但没有有关stdlib日志记录行为的帖子。

1 个答案:

答案 0 :(得分:2)

threading.current_thread()中的CPython stdlib version实际上没有锁定。您遇到的问题是Eventlet特有的,Eventlet会进行大量的猴子补丁操作以使线程系统混乱。尽管一种方法可能是停止使用eventlet,但这可能需要您重写整个应用程序,并且它无法解决您可能最终在登录时尝试锁定的任何其他方式,例如, __str__方法证明需要锁定。

最安全的登录方式__del__或在__del__中执行任何复杂的工作,最可能的方法是让__del__发送一条消息,告知其他代码以而是进行日志记录。这将在__del__与实际日志记录之间引入延迟,但是这种延迟在本质上是不可避免的,因为我们必须延迟日志记录直到它需要的资源可用为止。这也不能保证日志记录调用和__del__发生在同一线程上。在非eventlet上下文中,调用current_thread()以确定哪个线程正在处理__del__可能是安全的,但是对于eventlet,可能不是一个好方法。

大多数发送消息的方式都将具有类似的线程安全性或可重入性问题,但是Python 3.7使用put方法添加了queue.SimpleQueue类,并设计了可重入的方法。用它来管理您的__del__记录消息可能看起来像

import queue
del_log_queue = queue.SimpleQueue()

def stop_del_logging():
    del_log_queue.put(None)

...

def __del__(self):
    del_log_queue.put((tuple, of, relevant, information))

...

# in some other thread

while True:
    info = del_log_queue.get()
    if info is None:
        break
    relevant_logger.log(do_something_with(info))

在非Eventlet上下文中,使用logging.QueueHandlerlogging.QueueListener处理SimpleQueue可能是安全的,但是使用eventlet则无效,因为我们需要延迟创建LogRecord,直到我们用尽__del__