logging 日志输出乱码 info_【Python】logging 模块应用

本文介绍了Python logging模块的工作原理,包括Loggers、Handlers、Filters和Formatters的功能。讲解了如何配置RotatingFileHandler和TimeRotatingFileHandler进行日志轮换,以及如何使用SMTPHandler发送日志邮件。同时,文中提到了日志级别的设定规则,并讨论了在创建多个logger时可能出现的日志重复打印问题,以及通过colorlog模块实现彩色日志输出的优化方法。
摘要由CSDN通过智能技术生成
编写 Python 程序,经常需要在控制台打印输出,来了解程序的实际执行情况。 常用的打印输出方法 print(),在项目内使用,有时就不那么方便: 1. 调试阶段使用 print,发布时需要注释或删除不必要的打印信息,无法过滤; 2. 部分应用程序后台执行,没有控制台实时显示,就需要保存日志到文件; 3. 如果需要格式化输入日志,则需要重复编写格式代码。 所以 Python 标准库出现了更加灵活、功能丰富的 logging 模块,该模块定义函数和类,为应用程序和库实现了灵活的事件日志记录系统。 logging 模块化设计,主要包含四种组件:
  • Loggers:记录器,提供应用程序代码能直接访问使用的接口;
  • Handlers:处理器,将记录器产生的日志发送至目的地;
  • Filters:过滤器,提供更好的粒度控制,决定哪些日志将会被输出;
  • Formatters:格式化器,设置日志内容的组成结构和消息字段。

56ae27d823c82aac820af6897c82398f.png

工作原理

创建 logger  就相当于创建了一支虚拟的笔,可以创建多个,创建时需要设置一个默认输出等级,仅输出 ≥ 默认日志等级的信息; 指定 Handler 控制 这个笔的输出目的地,可以只写入控制台,也可以只写入文件内,还可以同时在控制台与文件内都写,等等,根据业务自己定义,可以分别设置日志等级;   创建 Formatter  设置日志输出的结构、格式,供处理器使用;处理器的 Handler 日志经过 Formatter 渲染后,绑定到记录器 logger 内; 程序调用 logger  此时程序就可以根据需求去调用不同的 logger,比如仅输出控制台、写入日志等等。 5867960f0e8bb300ea85ae611d3686f0.gif logger 记录器 logger = logging.getLogger(name) 通过 logging.getLogger(name) 函数实例化,用来创建记录器;多次调用 getLogger() 相同名称将始终返回对同一 Logger 对象的引用,也就是说 logger 是单例的。 logger.setLevel(level) 设置日志默认级别,如果不设置,默认级别为 warning logger.addHandler() | logger.removeHandler() 记录器可以绑定或移除不同的处理器,从而实现各种功能。比如:logger_a 仅输出到控制台;logger_b 仅把日志写入文件;logger_c 输入到控制台的同时写入文件;logger_d 发送邮件给指定用户等等。 5867960f0e8bb300ea85ae611d3686f0.gif Handlers 处理器 将日志分发到不同的目的地,可以是文件、标准输出、邮件或者通过 socke、http 等协议发送到任何地方。 sh = logging.StreamHandler(stream=None) 标准输出 stdout fh = logging.FileHandler(     filename, mode='a', encoding=None, delay=False ) 将日志保存到磁盘文件,mode 代表写入模式,‘a’  为追加 sh.setFormatter(color_formatter) fh.setFormatter(formatter) 设置 formatter,定义日志输出结构、格式,一个处理器仅能绑定一个 formattor。这里列出的只是常用的两个处理器,还有其他一些 ,比如:
  • RotatingFileHandler:按照文件大小,生成多个日志文件;

  • TimeRotatingFileHandler:按照时间,每天一个新文件等;

  • SMTPHandler:日志发送邮箱;

  • ...

5867960f0e8bb300ea85ae611d3686f0.gif Formatters 格式器 ft = logging.Formatter(     fmt=None, datefmt=None, style=' %' ) datefmt :日期格式,默认样式:%Y-%m-%d %H:%M:%S style :是格式化风格符,默认是百分号;使用示例:[%(message)s],这是设置所有输出的日志用 [] 括起来,这个参数不用修改,修改没有任何意义。 formattor 的格式化参数还有很多,如图(源码 485 行):

e0fb4ba5908bf0d310c508811498e454.png

5867960f0e8bb300ea85ae611d3686f0.gif Filters 过滤器 filter = logging.Filter("test") logger.addFilter(filter) 上面代码设置过滤器内的记录器为 “test”,则仅输出 “test” 记录器的日志; 如果没有创建过这个记录器,就不会输出任何日志;也可以对处理器设置过滤器,通过 addFilter 关联生效。 5867960f0e8bb300ea85ae611d3686f0.gif 日志级别 系统默认提供了 6 个日志级别,对应不同使用场景,如下:
# logging.py __init__ line:89

CRITICAL = 50
FATAL = CRITICAL # 致命错误,程序崩溃
ERROR = 40  # 程序错误,部分功能不能使用
WARNING = 30  # 警告信息,可能出现一些错误
WARN = WARNING
INFO = 20  # 程序正常运行时输出信息
DEBUG = 10  # 程序实现过程中调试信息
NOTSET = 0
另外,记录器和处理器可以分别选择设置默认日志级别,逻辑如下:
  • 如果没有给处理器指定日志级别,将使用记录器的日志级别;

  • 如果没有给记录器指定日志级别,将使用默认 warning 级别;

  • 如果记录器指定日志级别大于处理器指定日志级别,将以记录器日志级别为准;

  • 如果记录器指定日志级别小于处理器指定日志级别,将以处理器日志级别为准。

4bd988a476b68fd05a478f463519df2c.png Talk is cheap,show me the code. 
import os
import logging
import colorlog


class Logger(object):
    """初始化日志记录器
    usage:
        # 默认记录器是 root,会输出到控制台且写入到日志文件 output.log
        >>> logger1 = Logger().logger
        >>> logger1.debug("this is debug message")
        >>> import logging
        >>> logging.warning("this is warning message")
        # 创建记录器 test,会输出到控制台且写入到日志文件 test.log
        # 可以设置 level 级别
        >>> logger2 = Logger("test", level=logging.INFO).logger
        >>> logger2.error("this is error message")
        # 仅在控制台输出
        >>> logger3 = Logger(Logger.CONSOLE).logger
        >>> logger3.info("this is info message")
    """
    CONSOLE = "console"

    __colors_config = {
        'DEBUG': 'white', # cyan white
        'INFO': 'bold_blue',
        'WARNING': 'yellow',
        'ERROR': 'red',
        'CRITICAL': 'bold_red',
    }

    __date_fmt = "%y%m%d %H:%M:%S"
    __fmt = "[ %(levelname)1.1s %(asctime)s %(module)s:%(lineno)d ] %(message)s"
    __color_fmt = '%(log_color)s[ %(levelname)1.1s %(asctime)s %(module)s:%(lineno)d ]%(black)s %(message)s'
    __filepath = os.path.dirname(os.path.dirname(__file__)) + "/"

    def __init__(self, filename=None, level=logging.DEBUG):

        # 1. 创建记录器
        self.logger = logging.getLogger(filename) # 不填则为 root
        self.logger.setLevel(level)

        # 定义输出格式
        formatter = logging.Formatter(self.__fmt, datefmt=self.__date_fmt)
        color_formatter = colorlog.ColoredFormatter(
            fmt=self.__color_fmt,
            datefmt=self.__date_fmt,
            log_colors=self.__colors_config
        )

        # 2. 创建处理器并关联输出格式
        # 如果没有给处理器指定日志级别,将使用记录器的日志级别
        # 如果没有给记录器指定日志级别,那么会使用默认「warning」级别,处理器此时设置「debug」或「info」都不会输出
        # 2.1 创建 输出到控制台 处理器
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.INFO)
        console_handler.setFormatter(color_formatter)
        # 2.2 创建 输出到文件 处理器
        file_handler = logging.FileHandler(
            filename=self.__filepath + "output.log"
            if filename is None or Logger.CONSOLE else self.__filepath + filename + ".log"
        )
        file_handler.setLevel(logging.INFO)
        file_handler.setFormatter(formatter)

        # 3. 记录器关联处理器
        if filename == "console":
            self.logger.addHandler(console_handler)
        else:
            self.logger.addHandler(console_handler)
            self.logger.addHandler(file_handler)

        # 4. 过滤器 -- 仅输出设置的记录器下的日志,也可以对处理器进行设置过滤器
        # 这里设置为 test,就不会再输出日志了,因为只输出名为 "test" 记录器的日志
        # filter_app = logging.Filter("test")
        # self.logger.addFil(filter_app) # 关联过滤器


if __name__ == '__main__':
    # logging(默认记录器 root) 默认输出
    logging.info("this is default logging warning message")

    # 修改默认记录器 root,格式化输出
    logger1 = Logger().logger
    logger1.info("this is logger(root) info message")

    # 创建 console 记录器,仅输出到控制台
    logger2 = Logger(Logger.CONSOLE).logger
    logger2.error("this is logger(console) error message")

    # 创建 console 记录器,仅输出 ERROR 级别之上的日志
    logger4 = Logger(Logger.CONSOLE, level=logging.ERROR).logger
    logger4.info("logger4 > test info,You can't see me")
    logger4.error("logger4 > test error")

注意,我的测试代码,创建了多个记录器,当同时运行时,会发现有的日志多次打印,这是由于 logger 对象是有父子关系的。

以 logger1 和 logger2 为例:如果创建 console 记录器,则它的父对象就是 root,上面由于也创建了 root 记录器,所以父对象会同时收到日志,造成 console 的 logger 对象打印两遍日志。

为了控制台的日志更美观、易于分辨,我加入了定义日志颜色的 formattor,用到了 colorlog 模块,效果如图:

0779cc73974bf00160407cfaea9d0afb.png

以上就是关于 logging 模块,我的应用与理解,感谢阅读。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值