【问题标题】:How to separate log handlers in Python如何在 Python 中分离日志处理程序
【发布时间】:2019-09-19 23:37:15
【问题描述】:

我有一种情况,我想在 Python 中创建两个单独的记录器对象,每个对象都有自己独立的处理程序。 “分离”是指我希望能够独立地将日志语句传递给每个对象,而不会污染其他日志。

main.py

​​>
import logging
from my_other_logger import init_other_logger

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO, handlers=[logging.StreamHandler(sys.stdout)])

other_logger = init_other_logger(__name__)

logger.info('Hello World') # Don't want to see this in the other logger
other_logger.info('Goodbye World') # Don't want to see this in the first logger


my_other_logger.py

​​>
import logging
import os, sys

def init_other_logger(namespace):
    logger = logging.getLogger(namespace)
    logger.setLevel(logging.DEBUG)
    fh = logging.FileHandler(LOG_FILE_PATH)
    logger.addHandler(fh)
    formatter = logging.Formatter('%(levelname)s:%(name)s:%(message)s')
    fh.setFormatter(formatter)
    #logger.propagate = False
    return logger

我能确定在这里有用的唯一配置是logger.propagate 属性。按原样运行上面的代码将所有日志语句通过管道传输到日志流和日志文件。当我有logger.propagate = False 时,没有任何东西被传送到日志流,并且两个日志对象再次将它们的输出传送到日志文件。

如何创建一个仅将日志发送到一个处理程序的日志对象,以及另一个将日志发送到另一个处理程序的日志对象?

【问题讨论】:

    标签: python python-2.7 logging python-logging


    【解决方案1】:

    首先,让我们先看看发生了什么,然后再着手解决问题。

    logger = logging.getLogger(__name__) :当您这样做时,您将获取或创建一个名为'main' 的记录器。由于这是第一次调用,它将创建该记录器。

    other_logger = init_other_logger(__name__) :当你这样做时,你再次获取或创建一个名为 'main' 的记录器。由于这是 第二次 调用,它将获取上面创建的记录器。所以你并没有真正实例化一个新的记录器,但是你得到了对上面创建的同一个记录器的引用。您可以在调用init_other_logger 的形式后通过打印来检查这一点:print(logger is other_logger)。

    接下来发生的事情是将FileHandlerFormatter 添加到'main' 记录器(在init_other_logger 函数内),然后通过info() 方法调用2 个日志调用。但是您使用的是相同的记录器

    所以这个:

    logger.info('Hello World')
    other_logger.info('Goodbye World')
    

    本质上和这个是一样的:

    logger.info('Hello World')
    logger.info('Goodbye World')
    

    现在,两个记录器都输出到文件和流,这并不奇怪了。


    解决方案

    因此,显而易见的做法是用另一个名字给您的init_other_logger 打电话。

    我会推荐反对另一个答案提出的解决方案,因为那是 当您需要一个独立的记录器时应该如何做。 The documentation 说得很好,您应该绝不直接实例化记录器,但始终通过 logging 模块的函数 getLogger

    正如我们在上面发现的那样,当您调用logging.getLogger(logger_name) 时,它要么是获取 要么是创建 一个带有logger_name 的记录器。因此,当您也想要一个独特的记录器时,这非常有效。但是请记住,这个函数是 idemptotent 意味着它只会在您第一次调用它时创建一个具有给定名称的记录器,如果您使用相同的名称调用它,无论您调用多少次,它都会返回该记录器之后会调用它。

    所以,例如:

    • logging.getLogger('the_rock') 形式的第一次调用 - 创建您独特的记录器

    • logging.getLogger('the_rock') 形式的第二次调用 - 获取上述记录器

    您会发现,如果您:

    • 在项目中的某个位置(例如project_root/main_package/__init__.py)配置了一个记录器,其中包含FormattersFilters
    • 想要在位于 project_root/secondary_package/__init__.py 的辅助包中的某处使用该记录器。

    secondary_package/__init__.py 中,您可以简单地调用以下形式:logger = logging.getLogger('main_package'),然后您将使用该记录器及其所有功能。


    注意!

    即使此时您将使用您的init_other_logger 函数创建一个独特的记录器,它仍然会同时输出到文件和控制台。将此行 other_logger = init_other_logger(__name__) 替换为 other_logger = init_other_logger('the_rock') 以创建唯一的记录器并再次运行代码。您仍然会看到输出同时写入控制台和文件

    为什么?

    因为它将同时使用FileHandlerStreamHandler

    为什么?

    因为伐木机的工作方式。您的记录器将通过其处理程序发出其消息,然后它将一直传播到根记录器,在那里它将使用您通过basicConfig 调用附加的StreamHandler。因此,您发现的 propagate 属性 实际上是您想要的,因为您正在创建一个自定义记录器,您只想通过其手动附加的处理程序发出消息而不发出任何进一步。创建唯一记录器后取消注释logger.propagate = False,您会看到一切正常。

    【讨论】:

      【解决方案2】:

      您的两个处理程序都安装在同一个记录器上。这就是他们不分开的原因。

      logger is other_logger 因为logging.getLogger(__name__) is logging.getLogger(__name__)

      要么直接为第二个日志创建一个记录器logging.Logger(name)(我知道文档说永远不要这样做,但如果你想要一个完全独立的记录器,这就是这样做的方法),或者为第二个日志使用不同的名称拨打logging.getLogger()时。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-14
        • 2018-02-06
        • 1970-01-01
        • 2016-10-03
        • 1970-01-01
        • 1970-01-01
        • 2021-11-20
        • 2021-01-19
        相关资源
        最近更新 更多