python logging 模块配置咋不起作用了?

觉得太长?直接跳到结论部分试试!

1. 问题描述

日志可以说是所有程序都必不可少的组成部分,可是在有一天当笔者在程序入口写下如下代码时候,竟然不生效!?日志并不能如预想的一样,输出到 log_file_name

代码片段 1: 调用
...
01 logging.basicConfig(
02     level=logging.INFO,
03     handlers=[
04         logging.StreamHandler(),
05         TimedRotatingFileHandler(log_file_name, when='midnight', backupCount=7)
06     ],
07     format='%(asctime)s(%(process)d) %(filename)s > %(funcName)s > line %(lineno)d: %(levelname)s: %(message)s'
08  )
...

2. 问题分析

反复检查这段代码无误后,只能深入 logging.basicConfig 函数看看这个 python 自带的日志配置函数究竟做了什么(截取一部分)

代码片段 2: `logging.basicConfig` 源码,位于 `logging.__init__.py`
01  def basicConfig(**kwargs):
       ...
02     if len(root.handlers) == 0:
03         handlers = kwargs.pop("handlers", None)
           ...
04         if handlers is None:
05             filename = kwargs.pop("filename", None)
06             mode = kwargs.pop("filemode", 'a')
07             if filename:
08                 h = FileHandler(filename, mode)
09             else:
10                 stream = kwargs.pop("stream", None)
11                 h = StreamHandler(stream)
12             handlers = [h]
       ...
13          for h in handlers:
14              if h.formatter is None:
15                  h.setFormatter(fmt)
16              root.addHandler(h)

分析这段程序,02 行如果 root.handlers 为空,则会运行到 13~16 行给将每个 handler 添加到根 logger 的逻辑。所以真相只有一个,就是 root.handlers 不为空!

但是经过简单查看相关源码就会发现, root.handlers 在初始化的时候是一个空的列表,难道在我们代码的其他地方重复调用了 logging.basicConfig 方法?

经过全局搜索,确定只配置了一次日志,再次陷入僵局…

无奈,只能在 “代码片段 2” 第 02 行处打断点调试,想看看到底调用了几次该方法。神奇的事情发生了,果然调用了两次!符合猜想

那如果不是我自己代码调用的,是哪里调用的呢?利用 ide 的调用方查看功能,

诶?竟然是 logging.__init__.py 自己调用的?其中一处代码如下:

代码片段 3
...
01  def info(msg, *args, **kwargs):
02      """
03      Log a message with severity 'INFO' on the root logger. If the logger has
04      no handlers, call basicConfig() to add a console handler with a pre-defined
05      format.
06      """
07      if len(root.handlers) == 0:
08          basicConfig()
09      root.info(msg, *args, **kwargs)
...

原来是在调用 logging.info 方法时,如果还没有配置过日志,就会调用一次 logging.basicConfig 方法。

忽然想起自己在一个文件中,有一个预加载系统资源的功能,这个功能是直接在函数外面的,并且加载成功将打印日志。而我们知道,python 脚本的执行顺序是,如果你 import 了另一个文件,就会直接跳转到该文件,并将另一个文件执行完成,再跳转回来。也就是说,另一个文件中函数外的游离日志输出语句会优先于 main 的执行!造成了这次的问题。

想更直观的理解问题的原因,可以参考下图:
在这里插入图片描述

:图中步骤 3 并不是真的直接返回了 main.py,而是会顺序执行下方的两个函数定义和一个类定义,这全部执行完成之后才会返回 main.py 继续执行;同理步骤 4 也是一样,会执行完中间的各种定以后,才执行 main 方法.

3. 修复

知道了问题想要修复也很简单,一种方案是只需要在 “代码片段 1” 前面加上如下语句即可

01  # 清理已有 handlers
02  root_logger = logging.getLogger()
03  for h in root_logger.handlers:
04      root_logger.removeHandler(h)

这样,在执行 “代码片段 2” 第 02 行就就进入正常的逻辑

另一种方案是,将游离于函数的语句放在一个函数中,在配置完日之后再调用该函数

结论

问题原因:游离的日志输出语句(如 logging.info)提前触发了日志配置方法,使得logging.basicConfig 函数失效

解决方法:

  • 方案1:在调用 logging.basicConfig 方法前清除已有 handles
  • 方案2:通过合理方式组织和调用原有游离日志输出语句,将其执行进行延迟
  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
logging模块Python的内置模块,用于记录日志信息。它提供了一种灵活且可配置的方式,用于在应用程序中生成日志消息。通过使用logging模块,您可以根据不同的级别记录不同类型的消息,并且可以将这些消息输出到不同的目标,如控制台、文件或网络。 要使用logging模块,您需要先导入它: ```python import logging ``` 然后,您可以配置日志记录器并开始记录消息。以下是一个简单的示例,演示了如何使用logging模块记录日志消息: ```python import logging # 配置日志记录器 logging.basicConfig(level=logging.DEBUG, filename='app.log', filemode='w', format='%(asctime)s - %(levelname)s - %(message)s') # 记录不同级别的日志消息 logging.debug('This is a debug message') logging.info('This is an info message') logging.warning('This is a warning message') logging.error('This is an error message') logging.critical('This is a critical message') ``` 在上面的示例中,我们首先使用`basicConfig()`方法配置日志记录器。通过指定日志级别(例如`DEBUG`、`INFO`、`WARNING`、`ERROR`、`CRITICAL`),我们可以决定哪些级别的消息会被记录。我们还指定了日志文件的名称和格式。 然后,我们使用`debug()`、`info()`、`warning()`、`error()`和`critical()`方法来记录不同级别的日志消息。 您还可以在应用程序的其他位置使用相同的日志配置,并使用相同的记录器对象来记录消息。这样,您就可以在整个应用程序中保持一致的日志记录方式。 希望这个例子可以帮助您了解如何使用logging模块进行日志记录。如果您有任何进一步的问题,请随时提问!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值