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

f5emj3cl  于 2023-04-04  发布在  Python
关注(0)|答案(4)|浏览(769)

观察结果:当我注解掉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()
cbeh67ev

cbeh67ev1#

这是一个由两部分组成的答案:第一部分是关于你的问题,第二部分是关于为什么你的问题会出现在第一位(以及,好吧,我是如何结束这个帖子的,因为你的问题已经两个月了)。

**第一部分(回答你的问题):**当导入一个像import logging这样的包时,Python默认不会导入像logging.handlers这样的子包(或子模块),而只向你公开在包的__init__.py文件中定义的变量(在本例中是logging/__init__.py)。不幸的是,从外部很难分辨logging.handlerslogging/__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'。然而,在注解掉注解之后(对于Python〈3.9,在模块执行期间执行),调用函数可以工作-只要我称之为“足够晚”(下面有更多内容)。

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

那么“足够晚”是什么意思呢?不幸的是,我的应用程序太复杂了,无法快速查找bug。然而,我很清楚,logging.handlers必须在启动(即所有模块都被加载/执行)和调用该函数之间的某个时刻“变得可用”。这给了我决定性的提示:结果,包层次结构深处的另一个模块正在执行from logging.handlers import RotatingFileHandler, QueueListener。这条语句加载了整个logging.handlers模块,并导致Python将handlers模块“挂载”到其父包logging中,这意味着logging变量从此以后将始终配备handlers属性,即使在仅仅import logging之后,这就是为什么我不再需要import logging.handlers(这可能是PyCharm注意到的)。
自己试试看:
这是可行的:

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

这不会:

import logging
print(logging.handlers)

总而言之,这种现象是Python的导入机制使用全局状态避免重新加载模块的结果。(这里,我指的“全局状态”是当你import logging时得到的logging变量。)虽然有时候它是危险的(就像上面的例子一样),但它确实是完全有意义的:如果你在多个模块中使用import logging.handlers,你不希望logging.handlers模块中的代码被加载(并因此被执行!)多次。因此,一旦你在某处导入logging.handlers,Python就会将handlers属性添加到logging模块对象中,因为import logging的所有模块共享相同的logging对象,logging.handlers将突然无处不在。

gv8xihay

gv8xihay2#

也许你有一些其他的模块叫做logging,它屏蔽了标准库。在你的代码库中是否有一些叫做logging.py的文件?

lvmkulzt

lvmkulzt3#

如何诊断

解决这个问题的最佳方法是运行python3,然后执行以下命令:

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

这表明您正在使用这个OTHER包(my_personal_package)而不是内置的日志记录包。
它应该说:

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

你也可以把它放在一个程序中来调试它:

import logging
print(logging)

如何修复

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

62lalag4

62lalag44#

我在搜索类似的错误AttributeError: module 'logging' has no attribute 'config'时来到这里。我能够通过import logging.config修复它。
我建议你可以用import logging.handlers修复你的。我们需要这样做的原因是他们没有在logging.__init__.py中显式声明handlers,所以我们必须直接导入它-这也让我感到惊讶

相关问题