异步代码可以使用通常的日志记录功能,而无需借助特殊的异步模块或包装器。这样的代码是可能的。
import logging
:
async def do_some_async_stuff(self):
logging.getLogger(__name__).info("Started doing stuff...")
:
logging.getLogger(__name__).warn("Things went awry...")
这里的问题是提交日志条目是否会在将条目写入文件时产生一些延迟,从而剥夺异步系统在此期间运行其他任务的机会。如果将写入文件的阻塞处理程序直接添加到日志层次结构的某处,则可能会发生这种情况。
标准logging 模块为此提供了一个简单的解决方案:使用非阻塞处理程序将其消息排入在其自己的私有线程中运行的所需阻塞处理程序。
撇开纯粹主义不谈,没有硬性规则阻止使用 QueueHandler 提供异步代码,该代码使用非阻塞日志处理程序进行记录,并与托管在 QueueListener 中的阻塞处理程序一起使用。
以下解决方案与调用 logging 记录器并以典型方式提交条目的协程完全兼容 - 不需要调用 .run_in_executor() 的包装器。异步代码不会遇到来自日志系统的任何阻塞行为。
例如,QueueHandler 可以设置为根处理程序
import queue
from logging.handlers import QueueHandler
:
log_queue = queue.Queue()
queue_handler = QueueHandler(log_queue) # Non-blocking handler.
root = logging.getLogger()
root.addHandler(queue_handler) # Attached to the root logger.
您想要的阻塞处理程序可以放在QueueListener 中:
from logging.handlers import QueueListener
from logging.handlers import RotatingFileHandler
:
rot_handler = RotatingFileHandler(...) # The blocking handler.
queue_listener = QueueListener(log_queue,
rot_handler) # Sitting comfortably in its
# own thread, isolated from
# async code.
queue_listener.start()
然后使用您需要的任何日志条目格式配置嵌套在侦听器中的处理程序。
我个人喜欢旋转文件处理程序,因为它限制了生成的日志文件的大小和数量,在创建新备份时删除最旧的文件。