如何在 Python3 中使用多处理日志记录

时间:2020-12-28 19:24:11

标签: python-3.x logging multiprocessing

我正在尝试使用 python 内置的多处理日志记录。 目标——将错误记录到名为“error.log”的文件中 问题 - 错误打印在控制台而不是日志文件中。看下面的代码

import concurrent.futures
from itertools import repeat
import logging


def data_logging():
    error_logger = logging.getLogger("error.log")
    error_logger.setLevel(logging.ERROR)
    formatter = logging.Formatter('%(asctime)-12s %(levelname)-8s %(message)s')
    file_handler = logging.FileHandler('error.log')
    file_handler.setLevel(logging.ERROR)
    file_handler.setFormatter(formatter)
    error_logger.addHandler(file_handler)

    return error_logger


def check_number(error_logger, key):
    if key == 1:
        print ("yes")
    else:
        error_logger.error(f"{key} is not = 1")


def main():
    key_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 4, 5, 4, 3, 4, 5, 4, 3, 4, 5, 4, 3, 4, 3]
    error_logger = data_logging()
    with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor:
        executor.map(check_number, repeat(error_logger), key_list)


if __name__ == '__main__':
        main()

函数 check_number 检查列表 key_list 中的数字是否为 1 如果key = 1,则向控制台打印yes,如果不是,我希望程序将{key} is not = 1 添加到日志文件中。 而是使用上面的代码将其打印到控制台。如果可以,请帮忙。这是我程序的一个小例子,所以不要改变逻辑

1 个答案:

答案 0 :(得分:0)

能够将记录器实例传递给子进程,您必须一直使用python 3.7+。所以这里有一些关于事情是如何运作的。

基础

只能将可序列化的对象传递给子进程,或者换句话说,pickleable。这包括所有原始类型,例如 intfloatstr。为什么?因为python知道如何将它们重构(或解压)回子进程中的对象。

对于任何其他复杂的类实例,它是 unpickable,因为缺少有关该类的信息以从序列化字节重建其实例。 所以如果我们提供类信息,我们的实例可以是unpickable,对吗?

在某种程度上,是的。通过调用 ClassName(*parameters),它当然可以从头开始重建实例。那么,如果您在实例被腌制之前修改了实例,例如添加一些 __init__ 方法中没有的属性,例如 error_logger.addHandler(file_handler) 怎么办? pickle 模块不太聪明,无法了解您后来添加到实例中的所有其他内容。

为什么

那么,python 3.7+是如何pickle一个Logger实例的呢?它没有任何作用。它只是保存记录器的名称,它是一个纯 str。接下来,要解开,它只是调用 getLogger(name) 来重建实例。所以现在你明白了你的第一个复杂问题:子进程重建的记录器是一个没有附加任何处理程序的默认记录器,默认级别为 WARNING

方法

长话短说:使用 logger-tt。它支持开箱即用的多处理。

import concurrent.futures
from itertools import repeat
from logger_tt import setup_logging, logger


setup_logging(use_multiprocessing=True)


def check_number(key):
    if key == 1:
        print ("yes")
    else:
        logger.error(f"{key} is not = 1")


def main():
    key_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 4, 5, 4, 3, 4, 5, 4, 3, 4, 5, 4, 3, 4, 3]
    error_logger = data_logging()
    with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor:
        executor.map(check_number, key_list)


if __name__ == '__main__':
        main()

如果你想从头开始,还有一些问题需要解决:

  • 进程间通信:multiprocessing.Queuesocket
  • 使用 QueueHandlerQueueListener 进行日志记录
  • 将日志记录卸载到不同的线程或子进程

为了避免重复日志条目或丢失部分日志或根本没有日志,需要上述内容。

相关问题