【转载】Python 之 logging 使用分析

简介:


      昨天 Lord Leatherface 问我为什么使用了 suds 后,输出日志变成了两条。于是我仔细地研究了 logging 的日志处理机制,并且再次考虑了 uliweb 的日志处理,最终对 uliweb的 日志进行了重构,并且弄清楚了为什么会有两条日志的现象,因此以本文作一个记录。

对于 logging 我想大家用得应该不少,那么我先提几个问题:  
  • logging 直接输出日志,如 logging.info() 与 log = logging.getLogger('name') log.info() 有什么不同?
  • “No handlers could be found for logger”是怎么回事?
  • root logger 有什么用,如何获得?
  • 形如 "a.b.c" 的 logger 名有什么用?
  • 如果一个 logger 执行多次 addHandler,那么每个 handler 都会被执行吗?
  • logger 的传播(propagate)是怎么回事?如何阻止它?
有时候带着问题学习可能会更快。那么下面根据我的理解一点点进行解释。  
      logging 中 log 是可以分级的,它的级别与你使用 getLogger() 中的名字有关系。比如,你可以使用 "uliweb" 或 "uliweb.app",它们的区别就是 "uliweb.app" 中有一个 '.' 。那么 logging 会自动生成 "uliweb" 和 "uliweb.app" 的 logger,并且 "uliweb" 将是 "uliweb.app" 的 logger 的父对象。这一点可以这样验证:  
?
1
2
3
4
>>> import logging
>>> log = logging.getLogger( 'uliweb.app' )
>>> log.manager.loggerDict.items()
[( 'uliweb.app' , <logging.Logger instance at 0x017CE238 >), ( 'uliweb' , <logging.PlaceHolder instance at 0x017CE2D8 >)]
        这里有一个挺有意思的问题,就是 'uliweb' 这个 logger 它的对象是 PlaceHolder(占位对象),相当于是一个虚”日志对象。logging 可以允许通过 getLogger() 来自动生成对应的 logger 对象。对于分级的日志名字,它会一级级地创建,如果父对象还不存在,则会自动创建一个占位对象。如果后续用户又通过 getLogger() 创建一个父对象,则 logging 会自动对父子关系进行重新修订,保证对象关系的正确性。

再看一看父子关系:  
?
1
2
3
4
5
6
7
>>> log.parent
<logging.RootLogger instance at 0x017E3AD0 >
>>> log1 = logging.getLogger( 'uliweb' )
>>> log.parent
<logging.Logger instance at 0x01820DA0 >
>>> log.parent.name
'uliweb'
       可以看到,   当我们直接通过 getLogger('uliweb.app') 获得日志对象时,它的 parent 并不是占位对象 'uliweb',而是 RootLogger 。这个 RootLogger 是在导入 logging 时自动由模块创建的,它的创建代码是:  
?
1
2
3
root = RootLogger(WARNING)
Logger.root = root
Logger.manager = Manager(Logger.root)
        Logger 是真正的 logger 的类。可以看它在运行时会给它赋予 root 和 manager 对象。而 manager 就是用来创建 logger 并进行管理的类。前面的代码就是通过查看 manager.loggerDict 来查看所有已经定义的 logger 。因此从上面的代码我们可以理解,在直接创建一个多级的 logger 时,如果父对象不存在,则父对象自然是root 对象。如果父对象被创建,则父子关系被修正。

      root 对象有什么用呢?它就是缺省的日志对象。使用 logging 的最简单的方法是:  
?
1
2
import logging
logging.info()
可以直接输出。这里有什么秘密?看一看代码一切就都清楚了。  
?
1
2
3
4
5
6
7
def info(msg, * args, * * kwargs):
     """
     Log a message with severity 'INFO' on the root logger.
     """
     if len (root.handlers) = = 0 :
         basicConfig()
     root.info( * ((msg,) + args), * * kwargs)
        这里先对 root 的 handlers 进行判断。那么 handlers 是什么东西?它就是用来处理每条日志的处理类的实例。每个 logger 可以有不止一个 handler 实例。并且每个 handler 对象可以有自已的日志输出级别可以和logger 的不同。handler 的处理我们后面再说。上面的代码意思就是,如果 root 还没有定义处理对象,则执行basicConfig() 进行缺省配置。然后使用 root.info() 进行输出。所以我们可以理解:logging.info() 其实就是使用 root来进行输出的,你可以理解它是一个全局性的,缺省的日志对象。并且可以自动进行配置。那么,为什么要配置?因为 RootLogger(WARNING) 只是创建了 logger 类,但是还没有添加任何的 handler,因此handlers 是空的。也就是说,创建了一个 logger,并不表示它就可以输出日志。是不是有些奇怪?还是以代码为上:  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def callHandlers( self , record):
         c = self
         found = 0
         while c:
             for hdlr in c.handlers:
                 found = found + 1
                 if record.levelno > = hdlr.level:
                     hdlr.handle(record)
             if not c.propagate:
                 c = None    #break out
             else :
                 c = c.parent
         if (found = = 0 ) and raiseExceptions and not self .manager.emittedNoHandlerWarning:
             sys.stderr.write( "No handlers could be found for logger"
                              " \"%s\"\n" % self .name)
             self .manager.emittedNoHandlerWarning = 1
        上面的代码是 Logger 类中用来输出日志的一个方法,所有的秘密都在这里面了。这里不一条条解决了,说一下我的理解吧。它首先设置了一个 found 的标志,它的作用就是如果找到了一个可用的 handler,则加 1,然后等最后判断一下,如果 found 为 0,则表示你还没有对 logger 进行配置,在最后会输出“No handlers could be found for logger”的信息。还记得 root 的事情吗?创建了一个 logger 并不表示它就带有 handler了,也就是无法进行处理。因此 logging.info() 代码中,会当 root 没有配置 handler 时,使用 basicConfig()来进行配置。那么在 basicConfig() 的代码中,你会看到它会根据传入的参数自动生成相应的 Handler 实例,添加到 root 中去。所以 logging.info() 不会报错,是因为它自动配置了。而你自已通过 getLogger() 得到的某个日志对象,可没有自动化的配置方式,所以有可能会报错。因此现在我们可以就有一个印象,日志使用前要配置,主要是添加相应的 handler。那么这段代码还有什么特殊的?一个就是 c.propagate(propagate 的中文意思是传播),它是 Logger 的一个属性,缺省为 1。从上面的代码可以看出,如果 propagate 为 0 或假值,则循环就退出了。如果为 1,则循环会继续从父对象开始,直到找不到或 propagate 为假。因此 logging 会利用propagate 和父对象进行递归处理或传播处理。因此就样就实现这样一种效果:在传播情况下,从子结点到父结点的handler都会被处理一番。当然,还有一个检查点就是信息输出的日志级别要大于等于 handler 的日志级别。因此,如果父结点和子结点都定义了 handler,并且日志级别都符合要求,这样的确会输出两条日志,但是由不同的 handler 输出的。所以,如果子对象定义了自已的 handler,为了避免传播,因此应该设置它的propagate=0 。但是,利用传播,我们也可以实现,子日志对象不定义自已的 handler,因此最终是使用父对象的 handler。特别是对于父对象是 root 对象的情况,只要执行了 basicConfig(),则一定会存在 handler,就可以复用 root 的 handler 了。上面的代码还有一点要注意的就是,它会对 logger 的所有 handler 进行循环,当然同时要检查日志输出级别。因此,如果你为一个 logger 定义了多个 handler,那么就有可能都会输出。如果想区分不同的 handler,可以利用级别来控制。对于 root 对象,再说一点就是什么时候要执行 basicConfig()进行配置?如果你没有自定义 logger,只是使用 logging 中已经提供的如:info(), debug() 之类的方法,的确不必特别调用 basicConfig(),因为这些函数在执行时都做了检查,如果没通过,则自动会调用 basicConfig()。但是,如果你不是这样使用,而是使用自定义的 logger,因此还是建议你先执行 basicConfig() 进行缺省的配置。在使用自定义的 logger,要注意的问题就是:handler 的添加与 propagate 的处理。

      那么如果我想获得 root 对象该如何做呢?使用 logging.root 吗?常用的方法还是通过 getLogger('')来获得。参数可以是 '' 或其它为假的值。

      通过上面的讲解,我想你应该知道我提的几个问题的答案了吧。下面再简单总结一下:
  • logger 是分级的,有父子关系;
  • logger 的处理要靠 handler 来输出;
  • 获得了 logger 并不一定会自动创建 handler,因此 logger 一般都需要配置;
  • 在存在父子关系的情况下,日志是可以被传播输出的,并且可以通过 propagate 属性来控制。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
1月前
|
Shell 索引 Python
如何使用Python进行时间序列分析
如何使用Python进行时间序列分析
21 0
|
17天前
|
Python
【python】爬楼梯—递归分析(超级详细)
【python】爬楼梯—递归分析(超级详细)
|
30天前
|
索引 Python
如何在Python中,Pandas库实现对数据的时间序列分析?
Pandas在Python中提供强大的时间序列分析功能,包括:1) 使用`pd.date_range()`创建时间序列;2) 通过`pd.DataFrame()`将时间序列转为DataFrame;3) `set_index()`设定时间列作为索引;4) `resample()`实现数据重采样(如按月、季度);5) `rolling()`进行移动窗口计算,如计算移动平均;6) 使用`seasonal_decompose()`进行季节性调整。这些工具适用于各种时间序列分析场景。
28 0
|
30天前
|
Python
请解释Python中的主成分分析(PCA)以及如何使用Sklearn库实现它。
PCA是数据降维工具,Python中可通过Sklearn的PCA类实现。以下是一简例:导入numpy、PCA和iris数据集;标准化数据;创建PCA对象并设定保留主成分为2;用PCA对象处理数据;最后展示降维结果。示例使用鸢尾花数据集从高维降至二维。可按需调整参数以优化效果。
13 0
|
7天前
|
机器学习/深度学习 人工智能 算法
图像处理与分析:Python中的计算机视觉应用
【4月更文挑战第12天】Python在计算机视觉领域广泛应用,得益于其丰富的库(如OpenCV、Pillow、Scikit-image)和跨平台特性。图像处理基本流程包括获取、预处理、特征提取、分类识别及重建生成。示例代码展示了面部和物体检测,以及使用GAN进行图像生成。
|
1月前
|
存储 数据可视化 索引
如何使用Python的Statsmodels库进行时间序列分析?
如何使用Python的Statsmodels库进行时间序列分析?
17 0
|
2天前
|
机器学习/深度学习 算法 数据可视化
python用支持向量机回归(SVR)模型分析用电量预测电力消费
python用支持向量机回归(SVR)模型分析用电量预测电力消费
24 7
机器学习/深度学习 算法 Python
13 0
|
2天前
|
算法 数据可视化 Python
Python中LARS和Lasso回归之最小角算法Lars分析波士顿住房数据实例
Python中LARS和Lasso回归之最小角算法Lars分析波士顿住房数据实例
11 0
|
2天前
|
机器学习/深度学习 数据采集 数据可视化
Python数据处理与分析
【4月更文挑战第13天】Python在数据处理与分析中扮演重要角色,常用库包括Pandas(数据处理)、NumPy(数值计算)、Matplotlib和Seaborn(数据可视化)、SciPy(科学计算)、StatsModels(统计建模)及Scikit-learn(机器学习)。数据处理流程涉及数据加载、清洗、探索、特征工程、模型选择、评估与优化,以及结果展示。选择哪个库取决于具体需求和数据类型。
13 1

热门文章

最新文章