Python logging模块继承逻辑

0X00 起因

在使用一个第三方库时,在我定义的日志文件中一直有额外的日志输出,于是开始了DEBUG之路。

涉及到的相关知识:logger的继承关系、handler继承、basicConfig、导入另一个模块且该模块包含logger会引发什么现象

0X01 DEMO

可能起因中的话并不能很清晰的描述我到底碰到了什么情况,所以直接上代码,给几个DEMO,大伙可以分析分析代码的日志会怎么输出,输出到哪个文件,如果全部分析正确了,那么说明相关知识已经掌握了。如果分析不出来,那么就还没有弄清楚相关细节。以两个py文件为例,a.py和b.py,我们直接运行b.py,因为在b中会import a。

1.

a.py:

from logging.handlers import RotatingFileHandler
import logging


a_logger = logging.getLogger(__name__)
a_logger.setLevel(logging.WARNING)
formatter = logging.Formatter('%(asctime)s %(levelname)s [%(lineno)d]: %(message)s')
update_debug = RotatingFileHandler('./a.log', maxBytes=20*1024*1024, backupCount=5)
update_debug.setFormatter(formatter)
a_logger.addHandler(update_debug)

a_logger.warning('log from a')

b.py:

import logging
import a


logging.basicConfig(
    filemode='a',
    filename='./b.log'
)

logging.warning('log from b')

结果:a.log中记录log from a,b.log中记录log from b

2.

a.py:

from logging.handlers import RotatingFileHandler
import logging


a_logger = logging.getLogger(__name__)
a_logger.setLevel(logging.WARNING)
formatter = logging.Formatter('%(asctime)s %(levelname)s [%(lineno)d]: %(message)s')
update_debug = RotatingFileHandler('./a.log', maxBytes=20*1024*1024, backupCount=5)
update_debug.setFormatter(formatter)
a_logger.addHandler(update_debug)

a_logger.warning('log from a')

b.py:

import logging


logging.basicConfig(
    filemode='a',
    filename='./b.log'
)

logging.warning('log from b')

import a

结果:a.log中记录了log from a,b.log中先记录了log from b,再记录了log from a。

3.

a.py:

from logging.handlers import RotatingFileHandler
import logging


a_logger = logging.getLogger(__name__)
a_logger.setLevel(logging.WARNING)
formatter = logging.Formatter('%(asctime)s %(levelname)s [%(lineno)d]: %(message)s')
update_debug = RotatingFileHandler('./a.log', maxBytes=20*1024*1024, backupCount=5)
update_debug.setFormatter(formatter)
a_logger.addHandler(update_debug)


def func():
    a_logger.warning('log from a')

b.py:

import logging
import a


logging.basicConfig(
    filemode='a',
    filename='./b.log'
)

logging.warning('log from b')

a.func()

结果:a.log记录了log from a,b.log先记录了log from b,再记录了log from a

4.究极折磨

a.py:

import logging


logging.basicConfig(
    filemode='a',
    filename='./a.log'
)

a_logger = logging.getLogger(__name__)
a_logger.warning('log from a')

b.py:

import logging
import a


logger = logging.getLogger()
logger.warning('log from b')

结果:a.log先记录了log from a,再记录了log from b

0X02 分析

关于logging模块有几个重要的知识点,掌握之后,便可以很好的分析日志究竟是怎么记录的了

        1.默认情况下logger分为root logger和其他logger,basicConfig配置的就是root logger,创建的其他logger会自动继承于root logger,并且在调用其他logger记录日志时,会先调用logger自身配置的handler,然后调用父亲的handler。有点绕,给个例子:

import logging

#  设置root logger的handler为FileHandler,以a的模式将日志追加到a.log中
logging.basicConfig(
    filemode='a',
    filename='./a.log'
)

#  获取一个__name__的logger,该logger自动继承于root logger,并且该logger在日志记录时
#  先会调用自身的handler,然后调用root logger的handler。在此处logger并未设置handler,所以
#  只会调用root logger的handler,也就会将日志记录到a.log中了
a_logger = logging.getLogger(__name__)
a_logger.warning('log from a')

        2.当导入的模块中定义了logger时,并且在最外层就进行了日志记录,那么在导入之后就会直接记录日志,因为python导入模块会将模块外层代码执行。参考00X1 DEMO中的1和2两个例子,先import a和后import a的日志记录情况是不同的。那么如何规避这种情况了,那就是在函数中进行日志记录,那么只有在调用该函数时才会记录日志,import模块时不会记录。

        3.如何让每个logger只调用自己的handler,不要去调用父类的handler?添加一个配置即可:

logger.propagate = False

        4.创建的logger默认只会继承root logger,两个同级logger并不会自动产生继承关系,除非手动指定,例如:

import logging


a_logger = logging.getLogger('a')
b_logger = logging.getLogger('b')
print a_logger.parent
print b_logger.parent

a_logger和b_logger的parent都为logging.RootLogger object

    5.当你的模块需要被别人导入使用时,最好不要在你的模块中设置root logger的配置,否则会使日志记录混乱,或者加上propagate参数。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
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模块进行日志记录。如果您有任何进一步的问题,请随时提问!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值