观察:当我注释掉
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
。所以这应该可以回答你的问题。
第二部分:我最近注意到,每当我真正想使用
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
将突然在任何地方可用.
也许你有一些名为 logging
的
other模块,它掩盖了标准库模块。您的代码库中是否有一些名为
logging.py
的文件?
我在搜索类似错误时来到这里
AttributeError: module 'logging' has no attribute 'config'
。我能够通过import logging.config
修复它。
我建议你可以用
import logging.handlers
修复你的问题
我们需要这样做的原因是他们没有在 handlers
中明确声明 logging.__init__.py
,所以我们必须直接导入它 - 这也让我感到惊讶
解决问题的最佳方法是简单地运行
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
来修复此问题。
添加 importlogging.handlers 以使用这些方法