Python 日志记录-禁用来自导入模块的日志记录

我正在使用 Python 日志记录模块,希望禁用由我导入的第三方模块打印的日志消息。例如,我使用如下内容:

logger = logging.getLogger()
logger.setLevel(level=logging.DEBUG)
fh = logging.StreamHandler()
fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s')
fh.setFormatter(fh_formatter)
logger.addHandler(fh)

当我执行 logger.debug (“ my message!”)时,它会打印出我的调试消息,但是它也打印出来自我导入的任何模块的调试消息(例如请求和许多其他内容)。

我希望只看到来自我感兴趣的模块的日志消息。有没有可能让日志记录模块做到这一点?

理想情况下,我希望能够告诉日志记录程序从“ ModuleX,ModuleY”打印消息,并忽略所有其他消息。

我查看了下面的内容,但是我不希望在每次调用导入函数之前都必须禁用/启用日志记录: 日志-如何忽略导入的模块日志?

124535 次浏览

问题是,不带参数调用 getLogger将返回 日志记录器,因此当您将级别设置为 logging.DEBUG时,您也在为使用该日志记录器的其他模块设置级别。

您可以使用根日志记录器通过简单的 没有来解决这个问题。为此,只需将名称作为参数传递,例如模块的名称:

logger = logging.getLogger('my_module_name')
# as before

这将创建一个新的日志记录器,因此它不会无意中更改其他模块的日志记录级别。


显然,您必须使用 logger.debug而不是 logging.debug,因为后者是一个方便的函数,它调用根日志记录器的 debug方法。

这是在 高级日志记录教程中提到的。它还允许您以简单的方式知道哪个模块触发了日志消息。

@ Bakuriu 非常优雅地解释了这个功能。相反,可以使用 getLogger()方法检索和重新配置/禁用不需要的日志记录器。

我还想添加 logging.fileConfig()方法接受一个名为 disable_existing_loggers的参数,该参数将禁用以前定义的任何日志记录器(即在导入的模块中)。

如果您打算使用 pythonlogging包,那么在每个使用它的模块中定义一个日志记录器是一个常见的约定。

logger = logging.getLogger(__name__)

许多流行的 python 包都是这样做的,包括 requests。如果包使用此约定,则很容易为其启用/禁用日志记录,因为日志记录器名称将与包相同(或者将是该日志记录器的子日志记录器)。您甚至可以将其记录到与其他日志记录器相同的文件中。

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)


requests_logger = logging.getLogger('requests')
requests_logger.setLevel(logging.DEBUG)


handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
requests_logger.addHandler(handler)

这将禁用所有现有的日志记录器,例如由导入的模块创建的日志记录器,同时仍然使用根日志记录器(并且不必加载外部文件)。

import logging.config
logging.config.dictConfig({
'version': 1,
'disable_existing_loggers': True,
})

请注意,您需要导入所有不希望首先登录的模块!否则,这些日志不会被视为“现有的日志记录器”。然后它将禁用来自这些模块的所有日志记录器。这可能会导致您错过重要的错误!

有关使用相关选项进行配置的更详细示例,请参见 https://gist.github.com/st4lk/6287746,并且 给你是使用 YAML 进行 coloredlog库配置的(部分工作的)示例。

不知道这是否适合张贴,但我被困了很长时间,并希望帮助任何人与同样的问题,因为我没有发现它在其他地方!

我从 matplotlib 获得了调试日志,尽管在 < a href = “ https://docs.python.org/3.5/howto/logging.html # logging-Advanced-lessons”rel = “ noReferrer”> logging Advanced education 上有一些非常简单的文档 和 故障排除。我在一个文件的 main()中初始化我的日志记录器,并导入一个函数从另一个文件创建一个绘图(我在其中导入了 matplotlib)。

对我有效的方法是设置 matplotlib 之前导入它的级别,而不是像我在主文件中对其他模块那样设置之后的级别。这对我来说似乎是违反直觉的,所以如果有人知道如何设置一个尚未导入的日志记录器的配置,我会很好奇这是如何工作的。谢谢!

在我的主文件里:

import logging
import requests
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logging.getLogger('requests').setLevel(logging.DEBUG)


def main():
...

在我的 plot.py文件中:

import logging
logging.getLogger('matplotlib').setLevel(logging.WARNING)
import matplotlib.pyplot as plt


def generatePlot():
...

你可以使用这样的东西:

logging.getLogger("imported_module").setLevel(logging.WARNING)
logging.getLogger("my_own_logger_name").setLevel(logging.DEBUG)

这将把我自己的模块的日志级别设置为 DEBUG,同时防止导入的模块使用相同的级别。

注: "imported_module"可以替换为 imported_module.__name__(没有引号) ,而 "my_own_logger_name"可以替换为 __name__,如果你喜欢这样做的话。

我也有同样的问题。 我有一个 log _ config.py 文件,我将它导入到所有其他 py 文件中。 在 log _ config.py 文件中,我将根日志记录器日志记录级别设置为 ERROR (默认情况下是其警告) :

logging.basicConfig(
handlers=[
RotatingFileHandler('logs.log',maxBytes=1000, backupCount=2),
logging.StreamHandler(), #print to console
],
level=logging.ERROR
)

在其他模块中,我导入 log _ config.py 并声明一个新的 logger 并将其级别设置为 debug:

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)

这样,我在 py 文件中记录的所有内容都会被记录下来,但是在调试和信息级别由导入模块(如 urllib、 request、 boto3等)记录的内容不会被记录下来。如果这些导入模块中有一些错误,那么它将被记录,因为我将根日志记录器级别设置为 ERROR。

另一件需要考虑的事情是 Logger 类的 繁殖属性。

例如,用于处理肥皂调用的 py-suds 库,甚至将其放入 ERROR

logging.getLogger('suds.client').setLevel(logging.ERROR)
logging.getLogger('suds.transport').setLevel(logging.ERROR)
logging.getLogger('suds.xsdschema').setLevel(logging.ERROR)
logging.getLogger('suds.wsdl').setLevel(logging.ERROR)

记录关于 sxbasics.py 模块的日志,从而创建大量的日志

enter image description here

因为日志的传播在默认情况下是 True,所以设置为 False,相反,我恢复了514MB 的日志。

import logging
logging.getLogger("suds").propagate = False
logging.getLogger('suds.client').setLevel(logging.ERROR)
logging.getLogger('suds.transport').setLevel(logging.ERROR)
logging.getLogger('suds.xsdschema').setLevel(logging.ERROR)
logging.getLogger('suds.wsdl').setLevel(logging.ERROR)

简单地做这样的事情就可以解决问题:

logging.config.dictConfig({'disable_existing_loggers': True,})

注意 : 无论您在哪里放置这一行,请确保项目中的所有导入都在该文件本身内完成。:)

在尝试了这个帖子和其他论坛中的各种答案之后,我发现这种方法可以有效地屏蔽其他模块的日志记录器。这是受到以下链接的启发:

Https://kmasif.com/2019-11-12-ignore-logging-from-imported-module/

import logging
# Initialize your own logger
logger = logging.getLogger('<module name>')
logger.setLevel(logging.DEBUG)


# Silence other loggers
for log_name, log_obj in logging.Logger.manager.loggerDict.items():
if log_name != '<module name>':
log_obj.disabled = True


请注意,您希望在导入其他模块之后执行此操作。但是,我发现这是禁用其他模块的日志记录器的一种方便快捷的方法。

受到 @ Brendan 的回答的启发,我创建了一个简单的 logger_blocklist,我可以只是所有的记录器,我想提高的水平。因此,我能够一次性让他们都闭嘴:

import logging


logging.basicConfig(level=logging.DEBUG)


logger_blocklist = [
"fiona",
"rasterio",
"matplotlib",
"PIL",
]


for module in logger_blocklist:
logging.getLogger(module).setLevel(logging.WARNING)

您可以从 format="%(name)s"参数(也包括在 logging.basicConfig()中)中找到日志记录器的名称,例如: DEBUG:matplotlib.font_manager:findfont: score(FontEntry(fname='/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf', name='Ubuntu', style='normal', variant='normal', weight=400, stretch='normal', size='scalable')) = 10.05

在这种情况下,需要将日志记录器 matplotlib添加到块列表中。

@ Finn 的回答相反,它不需要在 main()之外,等等。

如果您导入的模块使用根日志记录器,那么您可以在根日志记录器上添加一个筛选器(https://docs.python.org/3/library/logging.html#filter-objects)来删除不需要的日志:

class FilterUnwantedRecords():
def filter(self, record):
if '3rdpartymodule' in record.pathname:
return False
return True


logging.getLogger().addFilter(FilterUnwantedRecords())
# mymodule.py


# Import 'logging' module from standard library
import logging


# Set all loggers to show only WARNING and above
logging.basicConfig(level=logging.WARNING)


# Create your module-specific logger (__name__ will be 'mymodule' in this example)
logger = logging.getLogger(__name__)


# Set your desired logging level for your logger
logger.setLevel(level=logging.DEBUG)


# Add handlers if you want
fh = logging.StreamHandler()
fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:% filename)s(%(process)d) - %(message)s')
fh.setFormatter(fh_formatter)
logger.addHandler(fh)

另一个示例,这次使用了一个 helper 函数和来自 有钱第三方模块的 RichHandler。

# loghelper.py
import logging
from rich.logging import RichHandler


def new_logger(logger_name: str) -> logging.Logger:
FORMAT = "%(message)s"
logging.basicConfig(
level=logging.WARNING, format=FORMAT, datefmt="[%X]", handlers=[RichHandler()]
)
log = logging.getLogger(logger_name)
# Add handlers ...
# ...
return log

然后将 helper 函数导入到模块中

# mymodule.py


from loghelper import new_logger


# Nice and tidy!
logger = new_logger(__name__)