观察:当我注释掉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()
这是一个由两部分组成的答案:第一部分涉及您的直接问题。第二部分涉及的问题是,为什么你的问题一开始就可能出现(以及,好吧,鉴于你的问题已经两个月了,我是如何出现在这个帖子中的(。
第一部分(您的问题的答案(:当导入像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
单独导入它。所以这应该能回答你的问题。
第二部分:我最近注意到,每当我真正想使用logging.handlers.QueueHandler
时,我的IDE(PyCharm(总是建议使用import logging
。出于某种神秘的原因(尽管我在第一部分说过(,它一直在起作用…嗯,大多数时候。
具体来说,在下面的代码中,类型注释会导致预期的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
那么";太迟了";意思是不幸的是,我的应用程序有点太复杂了,无法快速查找错误。然而,我很清楚logging.handlers
必须";变得可用";在启动(即加载/执行我的所有模块(和调用该函数之间的某个时刻。这给了我决定性的提示:事实证明,包层次结构中的另一个模块正在执行from logging.handlers import RotatingFileHandler, QueueListener
。该语句加载了整个CCD_ 21模块;安装件";它的父包logging
中的handlers
模块,这意味着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
将突然在任何地方都可用。
也许您有一些其他模块,称为logging
,它屏蔽了标准库模块。您的代码库中是否有一个名为logging.py
的文件?
我是在搜索类似错误AttributeError: module 'logging' has no attribute 'config'
时来到这里的。我能够通过import logging.config
修复它。
我建议你可以用import logging.handlers
修复你的我们需要这样做的原因是他们没有在logging.__init__.py
中明确声明handlers
,所以我们必须直接导入它——这也让我感到惊讶
如何诊断
解决问题的最佳方法是简单地运行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.py
或logging/__init__.py
来修复此问题。