Python - 异步日志记录

22

在运行系统代码时,我需要记录大量数据。有哪些日志记录包可以使用以实现高效的异步日志记录? Python 的标准日志记录包(https://docs.python.org/2/library/logging.html)默认是异步的吗?


2
不,它并不是这样的。但你可以很容易地编写自己的处理程序,将消息放入队列中,然后由另一个线程接收。 - Klaus D.
@KlausD. 你能否详细解释一下或者提供一些相关链接让我了解更多? - Ziva
如果是Linux系统,您可以直接使用syslog或syslog-ng,这样使用起来很快。 - ArmenB
3个回答

23

您可以使用一组n工作者,使用concurrent.futures.ThreadPoolExecutor执行logging.info()信息,其中n始终应该等于1:

import concurrent.futures 
import logging

executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) 

def info(self, msg, *args):
    executor.submit(logging.info, msg, *args)

1
你能否解释一下你的代码是用来做什么的,以及它与我的问题有什么关系吗? - Ziva
1
代码示例展示了如何按照您的要求组织“info”函数调用的异步队列。非常重要的是,将工作线程数设置为1,这将确保调用的顺序准确无误。如果工作线程数大于1,则可能会出现意外行为-结果日志中的行顺序将不正确。敬礼,安德鲁 - tas
5
请注意,如果在调用“info”之后更改正在记录的值,则无法确定将记录哪个版本。 - Troy Daniels

12

异步代码可以使用常规的日志记录功能,而无需借助特殊的异步模块或包装器。这样的代码是可能的。

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()

然后使用所需的日志条目格式配置嵌套在监听器中的处理程序。

我个人喜欢旋转文件处理程序,因为它限制了产生的日志文件的大小和数量,在创建新备份时删除最旧的文件。


为了获得一个完全工作的示例,您还需要从logging.handlers中导入QueueListener。 - Troy Daniels
你说得对,谢谢 @TroyDaniels - Todd
1
线程在Cpython中受到GIL的影响,它们并不是很舒适。理想的解决方案是在std库中拥有一个异步日志记录模块,等待处理程序的emit方法。 - Pynchia
这个解决方案并不是纯粹的。它提供了一种混合异步/线程化的日志记录解决方案。然而,与使用异步执行器来接口日志对象相比,它可能更具有性能优势,因为后者也涉及线程和GIL。 - Todd

3
这可以通过 Polog 库最简单地实现。该库最初是基于线程池的异步操作,您只需要启用此功能即可。
安装它:
$ pip install polog

"并使用:"
from polog import log, config, file_writer


config.add_handlers(file_writer('file.log')) # Add a file handler.
config.set(pool_size=2) # The size of the thread pool. By default it's 0, that means "not asynchronous".

log('message')

正如你所看到的,让它变得更简单是很困难的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接