zoukankan      html  css  js  c++  java
  • Python中日志模块的使用

    前言

    程序和脚本往往是无人值守运行的,一旦发生问题,就需要我们去追溯当时的情况来定位问题的原因。
    这便需要我们在程序和脚本中引入日志的功能。
    相比于print信息,使用logging日志有以下优点

    • 可以记录输出日志的时间、文件、函数以及代码行,甚至线程名和线程号
    • 可以分等级记录日志(调试级、信息级、警告级、错误级、严重错误级)
    • 即可以实时输出到屏幕,也可以输出到文件

    基本使用

    Python中使用自带的logging模块输出日志,logging模块的主要组件为logger日志记录器,如下图。

    基本使用如下

    import logging
    
    logging.debug('调试级别的日志')
    logging.info('信息级别的日志')
    logging.warning('警告级别的日志')    # 或logging.warn('警告级别的日志')
    logging.error('错误级别的日志')
    logging.critical('严重错误级别的日志')
    try:
        1/0
    except Exception as ex:
        logging.exception(ex)   # 错误级别的日志,显示多行回溯信息
    

    运行结果为

    WARNING:root:警告级别的日志
    ERROR:root:错误级别的日志
    CRITICAL:root:严重错误级别的日志
    ERROR:root:division by zero
    Traceback (most recent call last):
      File "<ipython-input-5-9be264a94d56>", line 9, in <module>
        1/0
    ZeroDivisionError: division by zero
    

    发现只显示warning、error、critical的日志,这是因为logging中的默认日志记录器(root logger)的level级别是logging.WARNING,即默认只显示警告级别以上的日志。

    修改日志等级

    我们可以使用logging.basicConfig来修改root logger的设置。

    import logging
    logging.basicConfig(level=logging.DEBUG)  # 配置全局root logger
    
    logging.debug('调试级别的日志')
    logging.info('信息级别的日志')
    logging.warning('警告级别的日志') 
    logging.error('错误级别的日志')
    logging.critical('严重错误级别的日志')
    

    再次运行,发现所有的日志都可以输出了。

    level日志等级支持

    • logging.NOTSET: 未设置,输出所有级别的日志
    • logging.DEBUG: 调试级别,输出所有级别的日志
    • logging.INFO: 信息级别,输出包含信息级别以上的日志
    • logging.WARNING: 警告级别,输出包含警告级别的日志
    • logging.ERROR: 错误级别,输出包含错误级别以上的日志
    • logging.CRITICAL: 严重错误级别,仅输出严重错误日志

    日志等级的关系为:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET

    修改日志格式

    日志默认的格式为WARNING:root:警告级别的日志,即等级:Logger名称:输出消息。我们也可以在logging.basicConfig中使用format参数来修改其输出格式。

    import logging
    
    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)s - %(levelname)s - %(message)s')
    logging.debug('调试级别的日志')
    logging.info('信息基本的日志')
    logging.warning('警告级别的日志')
    logging.error('错误级别的日志')
    logging.critical('严重错误级别的日志')
    

    运行输出如下:

    2020-11-04 10:11:49,238 - DEBUG - 调试级别的日志
    2020-11-04 10:11:49,238 - INFO - 信息基本的日志
    2020-11-04 10:11:49,238 - WARNING - 警告级别的日志
    2020-11-04 10:11:49,238 - ERROR - 错误级别的日志
    2020-11-04 10:11:49,238 - CRITICAL - 严重错误级别的日志
    

    format中使用%(变量名)s这样的具名占位符,来输出不同的信息,其他的字符则原样显示。
    支持的变量如下:

    • %(levelno)s: 打印日志级别的数值
    • %(levelname)s: 打印日志级别名称
    • %(pathname)s: 打印当前执行程序的路径,其实就是sys.argv[0]
    • %(filename)s: 打印当前执行程序名
    • %(funcName)s: 打印日志的当前函数
    • %(lineno)d: 打印日志的当前行号(因为是数字,所以使用%d)
    • %(asctime)s: 打印日志的时间
    • %(thread)d: 打印线程ID
    • %(threadName)s: 打印线程名称
    • %(process)d: 打印进程ID
    • %(message)s: 打印日志信息

    对于日期,我们可以使用datefmt来修改,比如,将上例中的logging.basicConfig修改为

    ...
    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)s - %(levelname)s - %(message)s',
                        datefmt='%Y年%m月%d日 %H:%M:%S')
    ...
    

    输出形式如下:

    2020年11月04日 10:19:04 - DEBUG - 调试级别的日志
    2020年11月04日 10:19:04 - INFO - 信息基本的日志
    2020年11月04日 10:19:04 - WARNING - 警告级别的日志
    2020年11月04日 10:19:04 - ERROR - 错误级别的日志
    2020年11月04日 10:19:04 - CRITICAL - 严重错误级别的日志
    

    输出到文件

    日志是默认输出的屏幕的,也可以通过logging.basicConfig中的filename将日志输出到文件。

    import logging
    
    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)s - %(levelname)s - %(message)s',
                        datefmt='%Y年%m月%d日 %H:%M:%S',
                        filename='run.log',
                        filemode='a'
                        )
    logging.debug('调试级别的日志')
    logging.info('信息基本的日志')
    logging.warning('警告级别的日志')
    logging.error('错误级别的日志')
    logging.critical('严重错误级别的日志')
    

    运行后,日志将不会输出到屏幕,转而输出到文件中,filemode支持'w'每次覆盖和'a'追加模式。

    自定义handlers

    如果想即输出到屏幕又输出到文件,我们可以在logging.basicConfig的handlers参数中添加两个不同的handler来实现,logging.StreamHander()可以用于输出到屏幕,logging.FileHandler()可以用于输出到文件。

    import logging
    
    cli_handler = logging.StreamHandler()  # 输出到屏幕的日志处理器
    file_handler = logging.FileHandler(filename='run.log', mode='a', encoding='utf-8')  # 输出到文件的日志处理器
    
    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)s - %(levelname)s - %(message)s',
                        datefmt='%Y年%m月%d日 %H:%M:%S',
                        handlers=[cli_handler, file_handler]   # 添加两个日志处理器
                        )
    logging.debug('调试级别的日志')
    logging.info('信息基本的日志')
    logging.warning('警告级别的日志')
    logging.error('错误级别的日志')
    logging.critical('严重错误级别的日志')
    

    这样便可以即输出到屏幕又输出到文件了。

    自定义Logger

    使用logging.basicConfig是直接配置全局的root logger,会对项目中所有的模块及三方包产生影响。
    为了不影响其他模块和三方包的日志输出,我们可以使用自定义Logger,即日志记录器,基本步骤为

    1. 使用logging.getLogger()新建一个logger对象,并配置期日志等级(总日志开关)
    2. 新建多个日志处理器,分别设置其格式和日志等级
    3. 将多个日志处理器添加到logger中
      代码如下
    # 文件名: mylogger.py
    import logging
    
    def get_logger(name):
        logger = logging.getLogger(name)
        logger.setLevel(logging.DEBUG)   # 设置总日志等级
    
        format = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y年%m月%d日 %H:%M:%S')  # 日志格式
    
        cli_handler = logging.StreamHandler()  # 输出到屏幕的日志处理器
        file_handler = logging.FileHandler(filename='run.log', mode='a', encoding='utf-8')  # 输出到文件的日志处理器
    
        cli_handler.setFormatter(format)  # 设置屏幕日志格式
        file_handler.setFormatter(format)  # 设置文件日志格式
    
        cli_handler.setLevel(logging.INFO)  # 设置屏幕日志等级, 可以大于日志记录器设置的总日志等级
        # file_hander.setLevel(logging.DEBUG)  # 不设置默认使用logger的等级
    
        logger.handlers.clear()  # 清空已有处理器, 避免继承了其他logger的已有处理器
        logger.addHandler(cli_handler)  # 将屏幕日志处理器添加到logger
        logger.addHandler(file_handler)  # 将文件日志处理器添加到logger
        return logger
    

    使用方式为,导入本模块的get_logger方法,使用logger代替logging打印各种信息。

    from mylogger import get_logger
    
    logger = get_logger('mylogger')
    
    logger.debug('调试级别的日志')
    logger.info('信息基本的日志')
    logger.warning('警告级别的日志')
    logger.error('错误级别的日志')
    logger.critical('严重错误级别的日志')
    

    注意:由于logger具有子模块继承性,在项目中多个地方使用get_logger生成不同的logger对象时,有可能会继承其他logger的处理器。建议项目中使用同一个logger。或者对日志记录器使用单例模式。
    在服务端项目中(如web项目),由于进程是长久运行,可以使用滚动日志处理器来分割日志文件。

  • 相关阅读:
    20145230《java学习笔记》第十周学习总结
    20145230《java学习笔记》第九周学习总结
    20145230《java程序设计》 第四次实验报告
    20145230《java程序设计》第三次试验报告
    20145230 《Java程序设计》第8周学习总结
    20145230java实验报告二
    20145230《java学习笔记》第七周学习总结
    20145230java实验报告1
    20145230《java程序设计》第6周学习总结
    20145228 《信息安全系统设计基础》第五周学习总结 (2)
  • 原文地址:https://www.cnblogs.com/superhin/p/13924876.html
Copyright © 2011-2022 走看看