Python 日志记录 - 属性错误:模块“日志记录”没有属性“处理程序”

问题描述 投票:0回答:5

观察:当我注释掉

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()
python logging handler
5个回答
20
投票

这是一个由两部分组成的答案:第一部分涉及您当前的问题。第二部分涉及为什么你的问题可能首先出现的问题(以及,考虑到你的问题已经两个月了,我是如何结束这个帖子的)。

第一部分(问题的答案): 当导入像

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.handlers
 单独导入。 from logging.handlers import TheHandlerYouWantToUse
。所以这应该可以回答你的问题。

第二部分:我最近注意到,每当我真正想使用

import logging
时,我的IDE(PyCharm)总是建议
logging.handlers.QueueHandler
。由于一些神秘的原因(尽管我在第一部分中说过)它一直在起作用! …嗯,大多数时候。

具体来说,在下面的代码中,类型注释导致了预期的

AttributeError: module 'logging' has no attribute 'handlers'
。但是,在注释掉注释之后(对于 Python < 3.9 gets executed during module execution time), calling the function works – provided I call it "late enough" (more on that below).

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
中,这意味着
logging
变量今后将始终配备
handlers
属性,即使在只是
import logging
,这就是为什么我不再需要
import logging.handlers
(这可能是 PyCharm 注意到的)。

自己尝试一下:

这有效:

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

这不是:

import logging
print(logging.handlers)

总而言之,这种现象是Python的导入机制使用全局状态来避免重新加载模块的结果。 (这里,我指的“全局状态”是当你

logging
时得到的
import logging
变量。)虽然它有时很危险(如上面的情况),但它确实很有意义:如果你
import logging.handlers
在多个模块中,您不希望
logging.handlers
模块中的代码多次加载(并因此执行!)。因此,只要您在某处导入
handlers
,Python 就会将
logging
属性添加到
logging.handlers
模块对象,并且由于
import logging
共享相同
logging
对象的所有模块,
logging.handlers
将突然在任何地方可用.


13
投票

也许你有一些名为 logging

other
模块,它掩盖了标准库模块。您的代码库中是否有一些名为
logging.py
的文件?


2
投票

我在搜索类似错误时来到这里

AttributeError: module 'logging' has no attribute 'config'
。我能够通过
import logging.config
修复它。

我建议你可以用

import logging.handlers
修复你的问题 我们需要这样做的原因是他们没有在
handlers
中明确声明
logging.__init__.py
,所以我们必须直接导入它 - 这也让我感到惊讶


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.py
logging/__init__.py
来修复此问题。


0
投票

添加 importlogging.handlers 以使用这些方法

© www.soinside.com 2019 - 2024. All rights reserved.