Python日志记录 - AttributeError:模块“logging”没有属性“handlers”。

12

观察:当我注释掉from logging import handlers时,会出现下面提到的错误。

Error:
    file_handler =  logging.handlers.RotatingFileHandler(
AttributeError: module 'logging' has no attribute 'handlers'

问题:如果我已经导入了logging,为什么还需要执行from logging import handlers

import logging
import sys
#from logging import handlers

def LoggerDefination():
    #file_handler = logging.FileHandler(filename='..\\logs\\BasicLogger_v0.1.log', mode='a')
    file_handler =  logging.handlers.RotatingFileHandler(
        filename="..\\logs\\BasicLogger_v0.2.log",
        mode='a',
        maxBytes=20000,
        backupCount=7,
        encoding=None,
        delay=0
    )
    file_handler.setLevel(logging.DEBUG)

    stdout_handler = logging.StreamHandler(sys.stdout)
    stdout_handler.setLevel(logging.DEBUG)
    handlers = [file_handler, stdout_handler]

    logging.basicConfig(
        level=logging.DEBUG,
        format='%(asctime)s | %(module)s | %(name)s | LineNo_%(lineno)d | %(levelname)s |  %(message)s',
        handlers=handlers
    )

def fnt_test_log1():
    LoggerDefination()
    WriteLog1 = logging.getLogger('fnt_test_log1')
    #WriteLog1.propagate=False
    WriteLog1.info("######## START OF : test_log1 ##########")
    WriteLog1.debug("test_log1 | This is debug level")
    WriteLog1.debug("test_log1 | This is debug level")
    WriteLog1.info("test_log1 | This is info level")
    WriteLog1.warning("test_log1 | This is warning level")
    WriteLog1.error("test_log1 | This is error level")
    WriteLog1.critical("test_log1 |This is critiacl level")
    WriteLog1.info("######## END OF : test_log1 ##########")


def fnt_test_log2():
    LoggerDefination()
    WriteLog2 = logging.getLogger('fnt_test_log2')
    WriteLog2.info("######## START OF : test_log2 ##########")
    WriteLog2.debug("test_log2 ===> debug")
    WriteLog2.debug("test_log2 | This is debug level")
    WriteLog2.debug("test_log2 | This is debug level")
    WriteLog2.info("test_log2 | This is info level")
    WriteLog2.warning("test_log2 | This is warning level")
    WriteLog2.error("test_log2 | This is error level")
    WriteLog2.critical("test_log2 |This is critiacl level")
    WriteLog2.info("######## STOP OF : test_log2 ##########")


if __name__ == '__main__':
    LoggerDefination()
    MainLog = logging.getLogger('main')
    LoggerDefination()
    MainLog.info("Executing script: " + __file__)
    fnt_test_log1()
    fnt_test_log2()

天哪,为什么你在每个函数调用中都配置日志记录?我建议使用搜索关键词“logging”浏览GitHub公共存储库并完成一些教程。 - madzohan
@amit chopra:我已经修复了你的问题格式。 - balu
4个回答

18

这是一个两部分的回答:第一部分回答你的直接问题。第二部分回答为什么你的问题可能首先出现(以及我如何进入这个线程,考虑到你的问题已经两个月了)。

第一部分(回答你的问题):当像import logging导入一个包时,默认情况下Python不会导入子包(或子模块),比如logging.handlers,而只会向您公开在包的__init__.py文件中定义的变量(在本例中为logging/__init__.py)。不幸的是,从外部很难判断logging.handlers是否是logging/__init__.py中的变量,还是一个实际的独立模块logging/handlers.py。因此,您必须查看logging/__init__.py,然后您将看到它未定义handlers变量,而是有一个名为logging/handlers.py的模块,您需要通过import logging.handlersfrom logging.handlers import TheHandlerYouWantToUse单独导入它。所以这应该回答了你的问题。

第二部分: 我最近注意到我的IDE(PyCharm)总是建议我导入import logging,每当我实际想使用logging.handlers.QueueHandler时。出于某种深奥的原因(尽管我在第一部分中所说的),它一直在工作!…嗯,大多数情况下。

具体来说,在以下代码中,类型注释会导致预期的AttributeError:module 'logging' has no attribute 'handlers'。但是,在注释掉注释后,调用该函数可以工作-前提是我调用得足够“晚”,关于这个后面再详细解释。

import logging
from multiprocessing.queues import Queue


def redirect_logs_to_queue(
    logging_queue: Queue, level: int = logging.DEBUG
) -> logging.handlers.QueueHandler:  # <-------------------------------- This fails

    queue_handler = logging.handlers.QueueHandler(logging_queue)  # <--- This works
    root = logging.getLogger()
    root.addHandler(queue_handler)
    root.setLevel(level)
    return queue_handler

那么“足够晚”是什么意思呢?不幸的是,我的应用程序有点过于复杂,无法进行快速的错误调试。尽管如此,对我来说很明显 logging.handlers 在启动(即加载/执行所有模块)和调用该函数之间的某个时刻必须“可用”。这给了我决定性的提示:原来,包层次结构中更深处的另一个模块正在执行 from logging.handlers import RotatingFileHandler, QueueListener。这个语句将整个 logging.handlers 模块加载,并导致 Python “挂载”handlers 模块在其父包logging中,这意味着即使仅使用 import logginglogging变量以后也始终会带有一个 handlers 属性,这就是为什么我不再需要 import logging.handlers(这也可能是 PyCharm 注意到的原因)。

您可以自己尝试:

这有效:

import logging
from logging.handlers import QueueHandler
print(logging.handlers)

这个不行:

import logging
print(logging.handlers)

总的来说,这种现象是Python使用全局状态避免重新加载模块的导入机制的结果。(在这里,我提到的“全局状态”是指当你导入logging时得到的logging变量。)虽然有时候它是具有欺骗性的(就像上面的情况一样),但它确实是很合理的:如果你在多个模块中导入logging.handlers,你不希望logging.handlers模块的代码被加载(并且执行!)多次。因此,Python会将handlers属性添加到logging模块对象中,只要你在某个地方导入logging.handlers。由于所有导入logging的模块共享同一个logging对象,所以logging.handlers会突然间无处不在。


1
值得注意的是,OP 的示例 (from logging import handlers) 依赖于本回答后半部分描述的完全相同的副作用。模块 logging.handlers 已经被 作为 handlers 导入,因此它及其内容可以通过该简单名称访问 (例如 handlers.RotatingFileHandler)。这实际上创建了一些名称空间污染。如果 OP 无论如何都想使用完全限定的名称,则更清晰的做法是 import logging.handlers - John Bollinger
1
还有一个有趣的事情是,StreamHandler在logging/init.py中而不是logging/handlers.py中。有时候最好不要知道底层的东西是如何交织在一起的,直到某些东西不能正常工作。 - Karolius

12

也许你有另一个称为 logging 的模块掩盖了标准库中的 logging 模块。你的代码库中是否存在名为 logging.py 的文件?


1

我在搜索类似错误AttributeError: module 'logging' has no attribute 'config'时来到这里。我通过import logging.config成功解决了这个问题。

我建议你可以通过import logging.handlers来解决你的问题。 我们需要这样做的原因是在logging.__init__.py中没有明确声明handlers,所以我们必须直接导入它 - 这也让我感到惊讶。


0

如何诊断

找出问题的最好方法是简单地运行python3,然后执行以下命令:

Python 3.8.10
>>> import logging
>>> logging
<module 'logging' from '/home/user/my_personal_package/logging/__init__.py'>

这表明您正在使用此其他包(my_personal_package)而不是内置的日志记录包。

应该说:

>>> import logging
>>> logging
<module 'logging' from '/usr/lib/python3.8/logging/__init__.py'>

你也可以将这个放入程序中进行调试:

import logging
print(logging)

如何修复

通过删除有问题的 logging.pylogging/__init__.py 文件来解决此问题。


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