• Python的日志输出


    logging模块

    一、 基础使用

    1、 简介

    程序都有记录日志的需求,并且日志中包含的信息既有正常的程序访问日志,还可能有错误、警告等信息输出。
    python的logging模块提供了标准的日志接口,可以通过它存储各种格式的日志,logging的日志分为debug(), info(), warning(), error() and critical()5个级别。
    python默认只打印warning级别以上的日志

    日志基础:

    两种记录日志的方式:

    • 第一种方式是使用logging提供的模块级别的函数
    • 第二种方式是使用Logging日志系统的四大组件

    其实模块级别的日志记录函数也是对logging日志系统相关类的封装。

    2、 常用函数

    模块级别的常用函数

    函数说明
    logging.debug(msg, *args, **kwargs)创建一条严重级别为DEBUG的日志记录
    logging.info(msg, *args, **kwargs)创建一条严重级别为INFO的日志记录
    logging.warning(msg, *args, **kwargs)创建一条严重级别为WARNING的日志记录
    logging.error(msg, *args, **kwargs)创建一条严重级别为ERROR的日志记录
    logging.critical(msg, *args, **kwargs)创建一条严重级别为CRITICAL的日志记录
    logging.log(level, *args, **kwargs)创建一条严重级别为level的日志记录
    logging.basicConfig(**kwargs)对root logger进行一次性配置

    默认只有warning级别以上的日志会打印

    3、 日志配置

    basicConfig所有参数信息

    参数名称描述
    filename指定日志输出目标文件的文件名
    filemode指定日志文件的打开模式,默认’a’
    format指定日志格式字符串
    datefmt指定日期/时间格式,需format中包含%(asctime)s字段
    level指定日志器的日志级别
    stream指定日志输出目标,如sys.stdoutsys.stderr以及网络streamstreamfilename不能共存
    style指定format格式字符串的风格,可取值为’%‘、’{‘和’$‘,默认为’%’
    handlers该选项如果应该是一个创建了多个Handler的可迭代对象,这些handler将会被添加到root logger

    format所有参数列表

    字段/属性名称使用格式描述
    asctime%(asctime)s字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”
    created%(created)f时间戳,就是调用time.time()函数返回的值
    msecs%(msecs)d日志发生时间的毫秒部分
    levelname%(levelname)s文字形式的日志级别
    levelno%(levelno)s数字形式的日志级别(10, 20, 30, 40, 50)
    name%(name)s使用的日志器名称,默认是’root’
    message%(message)s日志记录的文本内容,msg % args计算得到的
    pathname%(pathname)s调用日志记录函数的源码文件的全路径
    filename%(filename)spathname的文件名部分,含文件后缀
    module%(module)sfilename的名称部分,不包含后缀
    lineno%(lineno)d调用日志记录函数的源代码所在的行号
    funcName%(funcName)s调用日志记录函数的函数名
    process%(process)d进程ID
    processName%(processName)s进程名称
    thread%(thread)d线程ID
    threadName%(thread)s线程名称

    示例:

    #!/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo01.py"
    __time__ = "2022/8/4 10:38"
    
    import logging
    
    logging.basicConfig(level=logging.DEBUG, format='%(asctime)s | %(levelname)s | %(message)s', datefmt='%Y-%m-%d %I:%M:%S %p')
    # level 配置最低打印的日志级别
    # format 格式化输出子让孩子信息
    logging.error("error")
    logging.log(logging.DEBUG, "%s is %s old", 'Tom', '12')  # 如果信息里面有变量,可以通过增加参数数量的方法来输出完整内容
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    注意:

    • logging.debug()等方法的定义中,除了msgargs参数外,还有一个**kwargs参数。它们支持3个关键字参数:exc_info, stack_info, extra,下面对这几个关键字参数作个说明。
      • exc_info: 其值为布尔值,如果该参数的值设置为True,则会将异常信息添加到日志消息中。如果没有异常信息则添加None到日志信息中
      • stack_info:其值也为布尔值,默认值为False。如果该参数的值设置为True,栈信息将会被添加到日志信息中
      • extra:这是一个字典(dict)参数,它可以用来自定义消息格式中所包含的字段,但是它的key不能与logging模块定义的字段冲突

    二、 日志进阶

    1、 四大组件

    组件名称对应类名功能描述
    日志器Logger提供了应用程序可一直使用的接口
    处理器Handler将logger创建的日志记录发送到合适的目的输出
    过滤器Filter提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录
    格式器Formatter决定日志记录的最终输出格式

    组件之间的关系

    • 日志器(logger)需要通过处理器(handler)将日志信息输出到目标位置,如:文件、sys.stdout、网络等
    • 不同的处理器(handler)可以将日志输出到不同的位置
    • 日志器(logger)可以设置多个处理器(handler)将同一条日志记录输出到不同的位置
    • 每个处理器(handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志
    • 每个处理器(handler)都可以设置自己的格式器(formatter)实现同一条日志以不同的格式输出到不同的地方

    日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还可以通过过滤器(filter)和格式器(formatter)对要输出的日志内容做过滤和格式化等处理操作

    2、 Logger组件

    2.1 常用方法

    Logger对象有三个任务要做:

    • 向应用程序代码暴露几个方法,使应用程序可以在运行时记录日志消息
    • 基于日志严重等级(默认的过滤设施)或filter对象来决定要对哪些日志进行后续处理
    • 将日志消息传送给所有感兴趣的日志handlers

    Logger对象最常用的方法分为两类:配置方法 和 发送方法

    配置方法描述
    Logger.setLevel()设置日志器将会处理的日志消息的最低严重级别
    Logger.addHandler()Logger.removeHandler()为该logger对象添加 和 移除一个handler对象
    Logger.addFilter() Logger.removeFilter()为该logger对象添加 和 移除一个filter对象
    发送方法描述
    Logger.debug(), Logger.info(), Logger.warning(), Logger.error(), Logger.critical()创建一个与它们的方法名对应等级的日志记录
    Logger.exception()创建一个类似于Logger.error()的日志消息
    Logger.log()需要获取一个明确的日志level参数来创建一个日志记录

    Logger.exception()Logger.error()的区别在于:Logger.exception()将会输出堆栈追踪信息,另外一个通常只是在一个exception handler中调用该方法

    2.2 获取对象

    一种方式是通过Logger类的实例化方法创建一个Logger类的实例,更通常的方法是用logging.getLogger()方法
    logging.getLogger()方法有一个可选参数name,该参数表示将要返回的日志器的名称标识,如果不提供该参数,则其值为'root'。若以相同的name参数值多次调用getLogger()方法,将会返回指向同一个logger对象的引用。

    # 聊天工具的图形界面模块可以这样获得它的Logger:
    LOG = logging.getLogger(”chat.gui”)
    
    # 核心模块可以这样:
    LOG = logging.getLogger(”chat.kernel”)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.3 层级等级

    logger的层级结构与有效等级:

    • 层级结构
      logger的名称是一个以’.‘分割的层级结构,每个’.‘后面的logger都是’.'前面的loggerchildren
    • 有效等级
      如果一个logger上没有被明确设置一个level,那么该logger就是使用它parentlevel,直到找到个一个明确设置了level的祖先为止。root logger总是会有一个明确的level设置(默认为 WARNING)。当决定是否去处理一个已发生的事件时,logger的有效等级将会被用来决定是否将该事件传递给该loggerhandlers进行处理
    • 继承关系
      child loggers在完成对日志消息的处理后,默认会将日志消息传递给与它们的祖先loggers相关的handlers。因此不必所有loggers定义和配置handlers,只需要为一个顶层的logger配置handlers,然后按照需要创建child loggers就可足够了。可以通过将一个loggerpropagate属性设置为False来关闭这种传递机制,默认为True

    3、 Handler组件

    3.1 组件用途

    Handler对象的作用是(基于日志消息的level)将消息分发到handler指定的位置(文件、网络、邮件等)。Logger对象可以通过addHandler()方法为自己添加0个或者更多个handler对象。比如,一个应用程序可能想要实现以下几个日志需求:

    • 把所有日志都发送到一个日志文件中
    • 把所有严重级别大于等于error的日志发送到stdout(标准输出)
    • 把所有严重级别为critical的日志发送到一个email邮件地址

    这种场景就需要3个不同的handlers,每个handler负责发送一个特定级别的日志到一个特定的位置

    3.2 组件方法

    方法描述
    Handler.setLevel()设置handler将会处理的日志消息的最低严重级别
    Handler.setFormatter()为handler设置一个格式器对象
    Handler.addFilter()Handler.removeFilter()为handler添加 和 删除一个过滤器对象

    应用程序代码不应该直接实例化和使用Handler实例。因为Handler是一个基类,它只定义了所有handlers都应该有的接口

    常用的Handler

    Handler描述
    logging.StreamHandler将日志消息发送到输出到Stream,如std.out, std.err或任何file-like对象。
    logging.FileHandler将日志消息发送到磁盘文件,默认情况下文件大小会无限增长
    logging.handlers.RotatingFileHandler将日志消息发送到磁盘文件,并支持日志文件按大小切割
    logging.hanlders.TimedRotatingFileHandler将日志消息发送到磁盘文件,并支持日志文件按时间切割
    logging.handlers.HTTPHandler将日志消息以GET或POST的方式发送给一个HTTP服务器
    logging.handlers.SMTPHandler将日志消息发送给一个指定的email地址
    logging.NullHandler该Handler实例会忽略error messages

    具体使用可以参照官方文档:https://docs.python.org/zh-cn/3/library/logging.handlers.html

    4、 Formater组件

    4.1 用途语法

    日志的formatter是个独立的组件,可以跟handler组合。Formater对象用于配置日志信息的最终顺序、结构和内容。
    Formatter类的构造方法定义如下:

    logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
    
    • 1

    参数:

    • fmt:指定消息格式化字符串,如果不指定该参数则默认使用message的原始值
    • datefmt:指定日期格式字符串,如果不指定该参数则默认使用"%Y-%m-%d %H:%M:%S"
    • style:可取值为 ‘%’, ‘{‘和 ‘$’,如果不指定该参数则默认使用’%’

    4.2 使用案例

    #!/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo01.py"
    __time__ = "2022/8/4 10:38"
    
    import logging
    
    LOG = logging.getLogger()
    fh = logging.FileHandler("access.log")
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    fh.setFormatter(formatter)  # 把formater绑定到fh上
    LOG.addHandler(fh)
    LOG.warning("test")  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    5、 Filter组件

    Filter可以被Handler和Logger用来做比level更细粒度的、更复杂的过滤功能。Filter是一个过滤器基类,它只允许某个logger层级下的日志事件通过过滤。该类定义如下:

    class logging.Filter(name='A.B')
        filter(record)
    
    • 1
    • 2

    一个filter实例化时传递的name参数值为’A.B’,那么该filter将只允许名称为类似'A.B','A.B,C','A.B.C.D','A.B.D'loggers产生的日志记录通过过滤。如果name为空字符串,则允许所有的日志事件通过过滤。

    filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,返回值为非0表示可以通过过滤。

    若需要,可以在filter(record)方法内部改变该record,比如添加、删除或修改一些属性
    还可以通过filter做一些统计工作,如计算一个特殊的loggerhandler所处理的record数量等

    使用示例:

    class IgnoreBackupLogFilter(logging.Filter):
        """忽略带db backup 的日志"""
        def filter(self, record): # 固定写法
            return   "db backup" not in record.getMessage()
    
    logger.addFilter(IgnoreBackupLogFilter())  # 自定义过滤器
    logger.warning("start to run db backup job ....")
    logger.error("test error ....")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    三、 配置文件

    1. 使用配置方法的 Python 代码显式创建记录器,处理程序和格式化程序
    2. 创建日志记录配置文件并使用该fileConfig()功能读取它
    3. 创建配置信息字典并将其传递给 dictConfig()函数

    1、 conf文件

    [loggers]  # 设置两个日志记录器,root和core,用来区分运行的文件
    keys=root, core
    
    [handlers]  # 设置handles
    keys=consoleHandler,fileHandler
    
    [formatters] # 设置格式化处理
    keys=simpleFormatter
    
    [logger_root]  # 配置root日志输出
    level=DEBUG
    handlers=fileHandler
    
    [logger_core]  # 配置core的日志输出
    level=DEBUG
    # 输出最低级别为debug
    handlers=consoleHandler,fileHandler
    # 添加控制台输出和文件输出
    qualname=core
    # 配置输出名字,一定要和日志输出的同名,相当于实例化中的参数name,root可以不用配置
    propagate=0
    # 是否要传递给祖先处理器,如果为1,则会输出多遍,父类也会输出
    
    [handler_consoleHandler]  # 配置控制台输出consoleHandler
    class=StreamHandler
    level=WARNING
    formatter=simpleFormatter
    args=(sys.stdout,)
    
    [handler_fileHandler]  # 配置文件输出fileHandler
    class=FileHandler
    level=DEBUG
    formatter=simpleFormatter
    args=('test.log','a+')
    
    [formatter_simpleFormatter]  # 配置输出格式化simpleFormatter
    format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
    datefmt=%Y-%m-%d %I:%M:%S %p
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    使用方法:

    #!/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo.py"
    __time__ = "2022/8/4 11:42"
    
    import logging
    from logging.config import fileConfig, dictConfig
    
    fileConfig("config.conf")
    LOG = logging.getLogger("core")
    LOG.error("这个是core对象的输出哦")
    LOG = logging.getLogger()
    LOG.error("这个是root对象输出哦")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2、 yaml文件

    环境配置:pip install PyYAML

    我们先把上面的conf文件转换为yaml文件

    version: 1.0
    formatters:
      simpleFormatter:
        format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        datefmt: '%Y-%m-%d %I:%M:%S %p'
    handlers:
      fileHandler:
        class: logging.FileHandler
        level: DEBUG
        formatter: simpleFormatter
        filename: test.log
        mode: a
        encoding: utf8
      consoleHandler:
        class: logging.StreamHandler
        level: WARNING
        formatter: simpleFormatter
        stream: ext://sys.stdout  # 注意这个哦
    loggers:
      root:
        level: DEBUG
        handlers: [fileHandler]
      core:
        level: DEBUG
        handlers: [consoleHandler,fileHandler]
        qualname: core
        propagate: 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    使用方法:

    #!/usr/bin/python3
    # -*- coding: UTF-8 -*-
    __author__ = "A.L.Kun"
    __file__ = "demo.py"
    __time__ = "2022/8/4 11:42"
    import yaml
    import logging
    from logging.config import fileConfig, dictConfig
    
    # fileConfig("config.conf")
    with open("config.yaml", 'r', encoding='utf-8') as f:
        config = yaml.load(f.read(), yaml.FullLoader)
        dictConfig(config)
    
    LOG = logging.getLogger("core")
    LOG.error("这个是core对象的输出哦")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    RBD与Cephfs
    JVM 线上排查命令
    判断矩形与矩形、圆、三角形的相交问题
    存储用户密码应该使用什么加密算法?
    针对《VPP实现策略路由》的修正
    最新版Git安装指南使用指南
    vue+element ui读取excel文件
    计算机网络篇之端口
    《征服数据结构》栈
    FPGA数字电子技术复习笔记(一)verilog语法规则补充
  • 原文地址:https://blog.csdn.net/qq_62789540/article/details/126157460