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

新手上路,请多包涵

观察:当我注释掉 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 许可协议

阅读 1.7k
1 个回答

这是一个分为两部分的答案:第一部分涉及您的直接问题。第二部分涉及为什么您的问题可能首先出现的问题(以及,鉴于您的问题已经存在两个月了,我是如何结束这个话题的)。

第一部分(你的问题的答案): 当导入像 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

那么“够晚”是什么意思呢?不幸的是,我的应用程序有点太复杂,无法快速查找错误。尽管如此,我很清楚 logging.handlers 必须在启动(即加载/执行我的所有模块)和调用该函数之间的某个时间点“变得可用”。这给了我决定性的提示:原来,包层次结构深处的另一个模块正在做 from logging.handlers import RotatingFileHandler, QueueListener 。 This statement loaded the entire logging.handlers module and caused Python to “mount” the handlers module in its parent package logging , meaning that the 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 模块中的代码被加载(并因此执行!)不止一次。 And so Python just adds the handlers attribute to the logging module object as soon as you import logging.handlers somewhere and since all modules that import logging 共享相同的 logging 对象, logging.handlers 将突然随处可用。

原文由 balu 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题