python logging日志模块使用

实例选自:openpifpaf/log.py

参考:
博客1
博客2
博客3
logging模块提供了两种记录日志的方式:

第一种方式是使用logging提供的模块级别的函数,比如logging.basicConfig(**kwargs), logging.info()等;
第二种方式是使用Logging日志系统的四大组件,loggers,handlers,filters,formatters 。

关于logger的层级结构与有效等级的说明:
logger的名称是一个以’.‘分割的层级结构,每个’.‘后面的logger都是’.'前面的logger的children,例如,有一个名称为foo 的logger,其它名称分别为 foo.bar, foo.bar.baz 和 foo.bam都是 foo 的后代。

logger有一个"有效等级(effective level)"的概念。如果一个logger上没有被明确设置一个level,那么该logger就是使用它parent的level;如果它的parent也没有明确设置level则继续向上查找parent的parent的有效level,依次类推,直到找到个一个明确设置了level的祖先为止。需要说明的是,root logger总是会有一个明确的level设置(默认为WARNING)。当决定是否去处理一个已发生的事件时,logger的有效等级将会被用来决定是否将该事件传递给该logger的handlers进行处理。

child loggers在完成对日志消息的处理后,默认会将日志消息传递给与它们的祖先loggers相关的handlers。因此,我们不必为一个应用程序中所使用的所有loggers定义和配置handlers,只需要为一个顶层的logger配置handlers,然后按照需要创建child loggers就可足够了。我们也可以通过将一个logger的propagate属性设置为False来关闭这种传递机制。

propagate的作用在于, 它把子代的所有record都发送给了父代, 循环往复, 最终到达root, 如果我们在子代中设置了hander, 那么一个record就会被发送两次 。第一次是子代发送到stderr(默认), 第二次是使用root的Handler。

root logger是最顶层的logger,Python root logger 解密
例如
但是root logger的默认值没有任何Handler, 可是我们知道即便不配置root logger也默认会有终端信息输出的呀!那么是谁处理的我们的LogRecordne呢?其实默认的Handler是存放在logging.lastResort属性中,它会把信息输入到stderr。这也就解释了为什么当我们在Pycharm中运行Python脚本时, 它的默认设置stderr显示是红色字体。我们只需要添加一个Handler就可以了,当我们添加一个stdout的 StreamHandler, 这样信息就可以输入到stdout中了,这时候在Pycharm中输出的信息就是普通字体,而不是红色错误字体了。

下面给出一个日志模块使用实例,注意,logging信息打印输出顺序其实就是程序的执行顺序。

import logging


LOG = logging.getLogger(__name__) # 获取当前模块名当作此Logger的名字 # openpifpaf.log

def configure(args):
    # pylint: disable=import-outside-toplevel
    from pythonjsonlogger import jsonlogger
    from . import __version__ as VERSION
    # 此处使用logging的方式是使用Logging日志系统的四大组件。
    file_handler = logging.FileHandler(args.output + '.log', mode='w')
    file_handler.setFormatter(
        jsonlogger.JsonFormatter('(message) (levelname) (name) (asctime)'))
    stdout_handler = logging.StreamHandler(sys.stdout)

    # basicConfig()函数只用来配置RootLogger.而且因为RootLogger为其他Logger的根Logger,即系统的根logger
    # 因此当你使用这个配置后,【其他Logger都会】继承RootLogger的配置,
    # 除非其他位置的Logger设置了name,可以有自己的独立设置,从而不继承这个RootLogger。
    # logging.basicConfig()函数是一个一次性的简单配置工具使,也就是说只有在第一次调用该函数时会起作用,
    # 后续再次调用该函数时完全不会产生任何操作的,多次调用的设置并不是累加操作。

    # 我们不必为一个应用程序中所使用的所有loggers定义和配置handlers,只需要为一个顶层的logger配置handlers。
    # 即我们没必要配置子代的Handler, 因为最终虽有的record都会被转发到root, 我们只需要配置它就可以了.
    logging.basicConfig(handlers=[stdout_handler, file_handler])  # RootLogger的level默认是WARNING
    # logging.warning('Who am I?')  # 这样调用其实是在root logger位置输出
    log_level = logging.INFO if not args.debug else logging.DEBUG  # logging(没有提供名字)会直接设置RootLogger的状态

    # 为当前logger设置名称,可对每个模块设置独立的logger,通过name来进行区别.若当前模块没有设置,将与父模块保持一致
    # 提供了当前logger命名之后,其他文件夹下模块__name__属于openpifpaf.xxxx的都会受到当前这个parent的设置,而这个parent的名称就是'openpifpaf'
	# 若没有给出名字,比如logging.getLogger(''),则name的值是'root'
    logging.getLogger('openpifpaf').setLevel(log_level)
    # print(logging.getLogger('openpifpaf').parent) >>> rootLogger # 注意看这里
	# 如此以来,openpifpaf.xxx以下的子logger会一路向上找到openpifpaf,而此时openpifpaf这个级别的logger被设置了level,则不必再往上追溯rootLogger了
    # ############ 不用logging.basicConfig()的话,也可以替换成下面代码,是相同的效果 ###############
    # logger = logging.getLogger('openpifpaf')  # #创建logger对象
    # logger.setLevel(log_level)  # 设置logger的日志处理级别
    # logger.addHandler(stdout_handler)  # 将handler绑定到logger
    # logger.addHandler(file_handler)

    LOG.info({
        'type': 'process',
        'argv': sys.argv,  # 返回一个list,包含运行程序本身的名字,以及用户输入的命令行中给予的参数
        'args': vars(args),  # 返回一个dict, args命名空间的值组成的字典
        'version': VERSION,
        'hostname': socket.gethostname(),  # 比如我的服务器名"GO"
    })
    return log_level

你可能感兴趣的:(python)