观察:当我注释掉 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()
原文由 amit chopra 发布,翻译遵循 CC BY-SA 4.0 许可协议
这是一个分为两部分的答案:第一部分涉及您的直接问题。第二部分涉及为什么您的问题可能首先出现的问题(以及,鉴于您的问题已经存在两个月了,我是如何结束这个话题的)。
第一部分(你的问题的答案): 当导入像
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
单独导入。所以这应该回答你的问题。第二部分: 我最近注意到我的 IDE (PyCharm) 总是建议
import logging
每当我真正想使用logging.handlers.QueueHandler
。由于一些神秘的原因(尽管我在第一部分中说过)它一直在工作! ……好吧,大多数时候。具体来说,在以下代码中,类型注释导致预期的
AttributeError: module 'logging' has no attribute 'handlers'
。但是,在注释掉注释(对于 Python < 3.9 的版本在模块执行期间执行)之后,调用该函数有效——前提是我称它为“足够晚”(更多内容见下文)。那么“够晚”是什么意思呢?不幸的是,我的应用程序有点太复杂,无法快速查找错误。尽管如此,我很清楚
logging.handlers
必须在启动(即加载/执行我的所有模块)和调用该函数之间的某个时间点“变得可用”。这给了我决定性的提示:原来,包层次结构深处的另一个模块正在做from logging.handlers import RotatingFileHandler, QueueListener
。 This statement loaded the entirelogging.handlers
module and caused Python to “mount” thehandlers
module in its parent packagelogging
, meaning that thelogging
从此以后,变量将始终配备handlers
属性,即使仅在import logging
之后,这就是为什么我不再需要import logging.handlers
可能是 PyCharm 注意到的)。自己试试看:
这有效:
这不会:
总而言之,这种现象是Python的导入机制使用全局状态来避免重新加载模块的结果。 (在这里,我指的“全局状态”是
logging
变量,当你import logging
时得到。)虽然它有时是危险的(如上例),它确实很有意义:如果您import logging.handlers
在多个模块中,您不希望logging.handlers
模块中的代码被加载(并因此执行!)不止一次。 And so Python just adds thehandlers
attribute to thelogging
module object as soon as you importlogging.handlers
somewhere and since all modules thatimport logging
共享相同的logging
对象,logging.handlers
将突然随处可用。