【Python】logging日志库

文章目录

    • 简介
    • 记录器Logger
      • 创建记录器
      • 记录器名称
    • 处理器Handler
      • StreamHandler
      • FileHandler
      • NullHandler
      • WatchedFileHandler
      • 根据日志文件大小轮转 RotatingFileHandler
      • 根据文件创建时间轮转 TimedRotatingFileHandler
    • 过滤器Filter
    • 格式器Formatter
      • LogRecord的属性
    • 日志等级
      • 给记录器设置日志级别
      • 给处理器设置日志级别
    • 输出日志记录
      • 调用方式
      • 输出日志参数说明
        • exc_info实例(将异常信息添加到日志中)
        • stack_info实例(堆栈信息:异常的位置)
        • extra实例
    • 日志记录配置

简介

Logging模块是Python标准库自带的日志模块,通过日志记录程序的运行情况是一个开发的好习惯,对于错误排查和系统运维都有很大帮助。内置模块提供了四大组件:

  • 记录器(Logger):提供应用程序代码直接使用的接口。
  • 处理器(Handler):将日志记录(由记录器创建)发送到适当的目的地。
  • 筛选器(Filter):提供了更细粒度的功能,用于确定要输出的日志记录。
  • 格式器(Formatter):程序在最终输出日志记录的内容格式。

记录器Logger

创建记录器

logger = logging.getLogger()

记录器名称

需要定义多个日志器时,需要指定日志器名称,不然结果就演化成多份相同的日志器了(Handler叠加现象)

  • 若没有指定记录器名称,默认为root

    import logging
    
    logger = logging.getLogger()  # 记录器名称默认为root
    logger1 = logging.getLogger()  # 记录器名称默认为root
    logger2 = logging.getLogger()  # 记录器名称默认为root
    
    # 设置三个处理器handler
    console_handler = logging.StreamHandler()
    console_handler1 = logging.StreamHandler()
    console_handler2 = logging.StreamHandler()
    
    # 给三个相同名称的logger添加上处理器;可以理解成给一个日志器加三个处理器,这一个日志器可以用logger、logger1、logger2任意一个表示
    logger.addHandler(console_handler)
    logger1.addHandler(console_handler1)
    logger2.addHandler(console_handler2)
    
    # 设置一下格式
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    console_handler.setFormatter(formatter)
    console_handler1.setFormatter(formatter)
    console_handler2.setFormatter(formatter)
    
    # 输出日志记录
    logger1.warning("输出一条日志记录")
    
    """
    2023-06-22 09:28:50,102 - root - WARNING - 输出一条日志记录
    2023-06-22 09:28:50,102 - root - WARNING - 输出一条日志记录
    2023-06-22 09:28:50,102 - root - WARNING - 输出一条日志记录
    """
    
  • 通常开发过程中,需要指定记录器名器,来规避handler叠加现象

    logger = logging.getLogger(__name__)  # 日志器名称为包名
    logger1 = logging.getLogger("DIY")  # 日志器名称为DIY
    
    print(logger)  # 
    print(logger1)  # 
    

处理器Handler

处理器负责将日志消息发送到不同目标,例如:控制台,日志文件,电子邮件,等目标

处理器类型 描述
StreamHandler 将日志消息发送到流(默认是sys.stderr),类似文件的对象
FileHandler 将日志消息发送到磁盘文件
NullHandler 禁止任何日志消息的处理
WatchedFileHandler 将日志消息发送到文件,并监视文件的变化
SocketHandler 将日志消息发送到网络套接字
DatagramHandler 将日志消息作为UDP数据包发送到指定的主机和端口
SMTPHandler 将日志消息通过电子邮件发送
SysLogHandler 将日志消息发送到系统日志
NTEventLogHandler 将日志消息发送到Windows NT/2000/XP的事件日志
MemoryHandler 将日志消息缓存到内存中,并在达到指定容量时发送到其他处理器
RotatingFileHandler 将日志消息发送到文件,并在文件大小达到一定限制时进行轮转
TimedRotatingFileHandler 将日志消息发送到文件,并在时间间隔或文件大小达到一定限制时进行轮转
HTTPHandler 将日志消息发送到HTTP服务器
QueueHandler 将日志消息发送到队列中,供其他线程处理
QueueListener 从队列中获取日志消息,并将其发送到其他处理器

StreamHandler

日志记录发送到控制台中

  • 用法

    logging.StreamHandler(stream=None)
    
  • 参数说明

    • stream:默认为none,日志输出到sys.stderr;指定stream的话,则日志输出到指定的steam

FileHandler

日志记录发送到磁盘文件中

  • 用法

    logging.FileHandler(filename, mode='a', encoding=None, delay=False)
    
  • 参数说明

    • filename:只填写文件名称,则默认新增文件到当前工作目录;填写工作路径+文件名称,则新增到对应工作目录
    • mode:默认a模式,可自定义
    • encoding:默认为None,可自定义
    • delay:默认为False,为True时,将文件打开延迟到第一次调用时进行emit()

NullHandler

不做任何事情的处理器,防止sys.stderr在没有日志记录配置的情况下将库的已记录事件输出

  • 用法

    logging.NullHandler()
    

WatchedFileHandler

该处理器旨在在Unix / Linux下使用,它监视文件以查看自上一次发出以来是否已更改。(如果文件的设备或索引节点已更改,则认为该文件已更改。)如果文件已更改,则关闭旧文件流,并打开该文件以获取新的流,不适合在Windows下使用

  • 用法

    logging.handlers.WatchedFileHandler(filename, mode='a', encoding=None, delay=False)
    

根据日志文件大小轮转 RotatingFileHandler

日志记录到文件中,且支持指定日志文件大小,备份文件数量

  • 用法

    logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, 
    	backupCount=0, encoding=None, delay=False)
    
  • 参数说明

    • filenam:要写入的日志文件名。

    • mode:文件打开模式,默认为追加模式a。

    • maxBytes:日志文件大小,单位为字节。当达到该大小限制时,将触发文件轮转。默认值为0,表示没有大小限制。

    • backupCount:备份文件数量。默认值为0,表示不保留备份文件。

      假设backupCount设置为3,并且已经存在app.log、app.log.1、app.log.2、app.log.3四个文件。当app.log文件大小达到或超过maxBytes限制时,文件轮转会按照以下步骤进行:
      
      app.log.3文件将被删除。
      app.log.2将重命名为app.log.3。
      app.log.1将重命名为app.log.2。
      app.log将重命名为app.log.1。
      创建一个新的空app.log文件用于继续记录日志消息。
      现在,你将有app.log、app.log.1、app.log.2和app.log.3四个文件。旧备份文件的数字会递增,最新的日志总是记录在app.log文件中。
      
      当app.log再次达到或超过maxBytes限制时,将重复这个轮转过程,删除最旧的备份文件(此时是app.log.3),重命名其他备份文件,并创建一个新的空app.log文件。
      
    • encoding:文件编码方式。默认为None,表示使用系统默认编码。

    • delay:是否在打开文件之前延迟。默认为False,表示立即打开文件。

根据文件创建时间轮转 TimedRotatingFileHandler

日志记录到文件中,支持按时间间隔来更新日志

  • 用法

    logging.handlers.TimedRotatingFileHandler(filename, when='h', interval=1, 
    	backupCount=0, encoding=None, delay=False, utc=False, atTime=None)
    
  • 参数说明

    • filename:要写入的日志文件名。
    • when:指定时间间隔的单位。默认为'h',表示按小时进行轮转。其他可选值有'S'(秒)、'M'(分钟)、'D'(天)、'W0''W6'(每周的某天)、'midnight'(每天凌晨)。
    • interval:时间间隔的数值。默认为1,表示每个时间间隔进行轮转。
    • backupCount:要保留的备份文件的数量。默认值为0,表示不保留备份文件。
    • encoding:文件编码方式。默认为None,表示使用系统默认编码。
    • delay:是否在打开文件之前延迟。默认为False,表示立即打开文件。
    • utc:是否使用UTC时间。默认为False,表示使用本地时间。
    • atTime:指定在一天中的特定时间执行轮转。当when设置为'midnight'时,可以指定具体的时间,格式为'%H:%M:%S'

过滤器Filter

用于对日志消息进行筛选和过滤,决定哪些日志消息应该被记录下来,哪些应该被忽略。过滤器可以被应用于日志记录器(Logger)或处理器(Handler)。

  • 官网给出的示例

    import logging
    from random import choice
    
    class ContextFilter(logging.Filter):
        """
        This is a filter which injects contextual information into the log.
    
        Rather than use actual contextual information, we just use random
        data in this demo.
        """
    
        USERS = ['jim', 'fred', 'sheila']
        IPS = ['123.231.231.123', '127.0.0.1', '192.168.0.1']
    
        def filter(self, record):
    
            record.ip = choice(ContextFilter.IPS)
            record.user = choice(ContextFilter.USERS)
            return True
    
    if __name__ == '__main__':
        levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
        logging.basicConfig(level=logging.DEBUG,
                            format='%(asctime)-15s %(name)-5s %(levelname)-8s IP: %(ip)-15s User: %(user)-8s %(message)s')
        a1 = logging.getLogger('a.b.c')
        a2 = logging.getLogger('d.e.f')
    
        f = ContextFilter()
        a1.addFilter(f)
        a2.addFilter(f)
        a1.debug('A debug message')
        a1.info('An info message with %s', 'some parameters')
        for x in range(10):
            lvl = choice(levels)
            lvlname = logging.getLevelName(lvl)
            a2.log(lvl, 'A message at %s level with %d %s', lvlname, 2, 'parameters')
            
        
        
        """
        2023-06-22 10:58:48,965 a.b.c DEBUG    IP: 127.0.0.1       User: jim      A debug message
        2023-06-22 10:58:48,965 a.b.c INFO     IP: 192.168.0.1     User: sheila   An info message with some parameters
        2023-06-22 10:58:48,965 d.e.f WARNING  IP: 192.168.0.1     User: sheila   A message at WARNING level with 2 parameters
        2023-06-22 10:58:48,965 d.e.f DEBUG    IP: 123.231.231.123 User: jim      A message at DEBUG level with 2 parameters
        2023-06-22 10:58:48,965 d.e.f DEBUG    IP: 127.0.0.1       User: sheila   A message at DEBUG level with 2 parameters
        2023-06-22 10:58:48,965 d.e.f ERROR    IP: 123.231.231.123 User: sheila   A message at ERROR level with 2 parameters
        2023-06-22 10:58:48,965 d.e.f INFO     IP: 123.231.231.123 User: fred     A message at INFO level with 2 parameters
        2023-06-22 10:58:48,965 d.e.f INFO     IP: 127.0.0.1       User: sheila   A message at INFO level with 2 parameters
        2023-06-22 10:58:48,965 d.e.f WARNING  IP: 127.0.0.1       User: sheila   A message at WARNING level with 2 parameters
        2023-06-22 10:58:48,965 d.e.f DEBUG    IP: 127.0.0.1       User: sheila   A message at DEBUG level with 2 parameters
        2023-06-22 10:58:48,965 d.e.f CRITICAL IP: 192.168.0.1     User: fred     A message at CRITICAL level with 2 parameters
        2023-06-22 10:58:48,965 d.e.f WARNING  IP: 123.231.231.123 User: fred     A message at WARNING level with 2 parameters
        """
    

格式器Formatter

格式器可以初始化日志记录的内容格式,结合LogRecord对象提供的属性,可以设置不同的日志格式

每个处理器只能有一个formatter,当给处理器多次设置formatter时,以最后一个为准

  • 语法

    logging.Formatter(fmt=None, datefmt=None, style='%')
    
  • 参数说明

    • fmt:日志格式参数,默认为None,如果不特别指定fmt,则使用’%(message)s’格式
    • datafmt:时间格式参数,默认为None,如果不特别指定datafmt,则使用formatTime()文档中描述的格式。
    • style:风格参数,默认为’%’,也支持’$’,’{'格式

LogRecord的属性

属性 描述 类型 格式化符号
args 用于格式化消息的参数 元组 %(args)s
asctime 格式化的可读时间字符串 字符串 %(asctime)s
created 记录的创建时间(自epoch开始的秒数) 浮点数 %(created)f
exc_info 异常信息(sys.exc_info的返回值) 元组 %(exc_info)s
exc_text 异常信息的字符串表示 字符串 %(exc_text)s
filename 创建日志记录的源文件的文件名 字符串 %(filename)s
funcName 创建日志记录的函数名 字符串 %(funcName)s
levelname 日志级别名称 字符串 %(levelname)s
levelno 日志级别的数值 整数 %(levelno)s
lineno 创建日志记录的源文件的行号 整数 %(lineno)d
module 创建日志记录的模块名 字符串 %(module)s
msg 日志消息 字符串 %(message)s
msecs 毫秒部分的时间戳 整数 %(msecs)d
name 记录器名称 字符串 %(name)s
pathname 创建日志记录的源文件的完整路径 字符串 %(pathname)s
process 进程ID 整数 %(process)d
processName 进程名称 字符串 %(processName)s
relativeCreated 记录相对于logging模块加载的时间(以毫秒为单位) 浮点数 %(relativeCreated)f
stack_info 栈信息字符串 字符串 %(stack_info)s
thread 线程ID 整数 %(thread)d
threadName 线程名称 字符串 %(threadName)s

日志等级

指定了日志等级后,只会显示大于等于所指定日志等级的日志信息!

logging中级别大小:DEBUG

日志等级(level) 描述 数值
0
DEBUG 调试信息,通常在诊断问题的时候用 10
INFO 普通信息,确认程序按照预期运行 20
WARNING(默认级别) 警告信息,表示发生意想不到的事,或者指示接下来可能会出现一些问题,但是程序还是继续运行 30
ERROR 错误信息,程序运行中出现了一些问题,程序某些功能不能执行 40
CRITICAL 危险信息,一个严重的错误,导致程序无法继续运行 50

给记录器设置日志级别

记录器设置的日志级别会作用于该记录器下所有的处理器

import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

给处理器设置日志级别

如果同时设置了记录器、处理器的日志级别,那么以最高的那个级别作为标准输出

import logging

logger = logging.getLogger(__name__)

# 设置两个处理器
console_handler = logging.StreamHandler()
console_handler1 = logging.StreamHandler()

# 设置处理器日志级别分别INFO,WARNING
console_handler.setLevel(logging.INFO)
console_handler1.setLevel(logging.WARNING)

输出日志记录

logging模块提供5个日志级别的输出方法(debug、info、warning、erro、critical)

调用方式

  • 直接通过logging调用

    import logging
    
    logging.error("aaaaa")
    
  • 通过创建记录器对象

    import logging
    
    logger = logging.getLogger('logger1')  # 获取配置中的logger对象
    # 输出logger日志记录
    logger.error("====================【开始测试】====================")
    

输出日志参数说明

  • 语法

    import logging
    
    logger = logging.getLogger('logger1')
    logger.debug(msg, *args, **kwargs)
    
  • 参数

    • msg:日志消息格式字符串
    • args:args是被合并到参数 MSG使用字符串格式化操作,当未提供args时,不会对msg执行%格式化操作。
    • kwargs:kwargs中检查了三个关键字参数: exc_info,stack_info和extra。
      • exc_info:是否将异常信息添加到日志消息的参数,默认为False,若不为False,则将异常信息添加到日志消息中。
      • stack_info:是否将堆栈信息添加到日志消息的参数,默认为False,若不为False,则将堆栈信息添加到日志消息中
      • extra:可用于传递字典,该字典用于使用LogRecord 用户定义的属性填充为日志记录事件创建的__dict__ 。然后可以根据需要使用这些自定义属性。

exc_info实例(将异常信息添加到日志中)

import logging

logger = logging.getLogger('logger1')  # 获取配置中的logger对象
logger.setLevel(logging.INFO)

console_handler = logging.StreamHandler()
# console_handler.setLevel(logging.INFO)

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)

logger.addHandler(console_handler)
try:
    open("notreal.txt", "rb")
except Exception:
    logger.info("Faild to open notreal.txt from logger.debug", exc_info=True)
    
    
"""
2023-06-22 11:42:14,166 - logger1 - INFO - Faild to open notreal.txt from logger.debug
Traceback (most recent call last):
  File "/Users/meta/GitHub/flaskProject/conf/log_conf.py", line 14, in 
    open("notreal.txt", "rb")
FileNotFoundError: [Errno 2] No such file or directory: 'notreal.txt'
"""

stack_info实例(堆栈信息:异常的位置)

logger.info("====================【开始测试】====================", stack_info=True)



"""
2023-06-22 11:46:23,240 - logger1 - INFO - ====================【开始测试】====================
Stack (most recent call last):
  File "/Users/meta/GitHub/flaskProject/conf/log_conf.py", line 19, in 
    logger.info("====================【开始测试】====================", stack_info=True)
"""

extra实例

FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'
logging.basicConfig(format=FORMAT)
d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}
logger = logging.getLogger('tcpserver')
logger.warning('Protocol problem: %s', 'connection reset', extra=d)


"""
2023-06-22 11:48:19,350 192.168.0.1 fbloggs  Protocol problem: connection reset
"""

日志记录配置

logging模块提供一个配置方法,以字典为容器,设置日志器、处理器、格式器等参数,使用logging.config.dictConfig(配置字典)方法生成对应的元器件

  • 方式一:

    import logging.handlers
    import logging.config
    
    config = {
        'version': 1,  # 必填项,值只能为1
        'disable_existing_loggers': True,  # 选填,默认为True,将以向后兼容的方式启用旧行为,此行为是禁用任何现有的非根日志记录器,除非它们或它们的祖先在日志配置中显式命名。如果指定为False,则在进行此调用时存在的记录器将保持启用状态
        'incremental': False,  # 选填,默认为False,作用,为True时,logging完全忽略任何formatters和filters,仅处理handlers的level
    
        'formatters':  # 格式器配置专用key,在这里配置formatter,可配置复数formatter
            {
                'myformatter1': {
                    'class': 'logging.Formatter',  # 必填,格式器对应的类
                    'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s',  # fmt格式
                    'datefmt': '%Y-%m-%d %H:%M:%S'  # 日期时间格式
                },
                # 'myformatter2': {
                #     '()': 'my_diy_formatter',  # 将class改为(),代表不使用logging的类,使用我们重新定义的类
                #     'format': '%(asctime)s - %(levelname)s - %(message)s',  # fmt格式
                #     'datefmt': '%Y-%m-%d %H:%M:%S'  # 日期时间格式
                # }
            },
    
        'handlers':  # 处理器配置专用key,在这里配置handler,可配置复数handler
            {
                'console_handler': {
                    'class': 'logging.StreamHandler',  # 必填,处理器对应的类
                    'level': logging.DEBUG,  # 选填,处理器的日志级别,可填字符串'info',或者logging.INFO
                    'formatter': 'myformatter1',  # 选填,这里要填写formatters字典中的键
                },
                'file_handler': {
                    'class': 'logging.handlers.RotatingFileHandler',  # 必填,处理器对应的类
                    'level': logging.INFO,  # 选填,处理器的日志级别,可填字符串'info',或者logging.INFO
                    'formatter': 'myformatter1',  # 选填,这里要填写formatters字典中的键
                    'filename': './mylog.log',  # filehandler特有参数,文件名
                    'maxBytes': 1024*1024,  # 文件大小
                    'backupCount': 3,  # 备份数量
                    'encoding': 'UTF-8',  # 编码格式
                }
            },
    
        'loggers':  # 记录器配置专用key,在这里配置logger,可配置复数logger
            {
                'logger1': {
                    'handlers': ['console_handler', 'file_handler'],  # 列表形式,元素填handlers字典中的handler
                    'level': logging.DEBUG,  # 选填,记录器的日志级别,不填则默认Warning级别
                    'propagate': False,  # 选填,为False时,禁止将日志消息传递给父级记录器
                }
            },
    
        'root':  # 根记录器专用key
            {
                'handlers': ['console_handler', 'file_handler'],  # 列表形式,元素填handlers字典中的handler
                'level': logging.DEBUG,  # 选填,记录器的日志级别,不填则默认Warning级别
            }
    }
    # 根据配置字典,配置对应元器件
    logging.config.dictConfig(config)
    
    if name == 'main':
        logger = logging.getLogger('logger1')  # 获取配置中的logger对象
        # 输出logger日志记录
        logger.debug("====================【开始测试】====================")
        logger.info("====================【开始测试】====================")
        logger.warning("====================【开始测试】====================")
        logger.error("====================【开始测试】====================")
        logger.critical("====================【开始测试】====================")
    
    
  • 方式二

    # json文件
    {
      "version": 1,
      "disable_existing_loggers": true,
      "incremental": false,
      "formatters": {
        "formatter1": {
          "class": "logging.Formatter",
          "format": "%(asctime)s - %(name)s - %(process)d - %(thread)d - %(message)s",
          "datefmt": "%Y-%m-%d %H:%M:%S"
        }
      },
      "handlers": {
        "console_handler": {
          "class": "logging.StreamHandler",
          "level": "DEBUG",
          "formatter": "formatter1"
        },
        "info_handler": {
          "class": "logging.handlers.RotatingFileHandler",
          "level": "INFO",
          "formatter": "formatter1",
          "filename": "{{ROOT_PATH}}/logs/info.log",
          "maxBytes": 1048576,
          "backupCount": 3,
          "encoding": "UTF-8"
        },
        "warning_handler": {
          "class": "logging.handlers.RotatingFileHandler",
          "level": "WARNING",
          "formatter": "formatter1",
          "filename": "{{ROOT_PATH}}/logs/warning.log",
          "maxBytes": 1048576,
          "backupCount": 3,
          "encoding": "UTF-8"
        },
        "error_handler": {
          "class": "logging.handlers.RotatingFileHandler",
          "level": "ERROR",
          "formatter": "formatter1",
          "filename": "{{ROOT_PATH}}/logs/error.log",
          "maxBytes": 1048576,
          "backupCount": 3,
          "encoding": "UTF-8"
        }
      },
      "loggers": {
        "log.debug": {
          "handlers": [
            "console_handler"
          ],
          "level": "DEBUG",
          "propagate": false
        },
        "log.info": {
          "handlers": [
            "info_handler"
          ],
          "level": "INFO",
          "propagate": false
        },
        "log.warning": {
          "handlers": [
            "warning_handler"
          ],
          "level": "WARNING",
          "propagate": false
        },
        "log.error": {
          "handlers": [
            "error_handler"
          ],
          "level": "ERROR",
          "propagate": false
        }
      },
      "root": {
        "handlers": [
          "console_handler"
        ],
        "level": "DEBUG"
      }
    }
    
    
    # 自定义封装一个类
    import json
    import logging.config
    
    from apps.src.utils.const import ROOT_PATH
    
    with open(f"{ROOT_PATH}/conf/log.json", "r") as f:
        log_json_str = f.read().replace("{{ROOT_PATH}}", ROOT_PATH)
        log_json = json.loads(log_json_str) if log_json_str else {}
    # 根据配置字典,配置对应元器件
    logging.config.dictConfig(log_json)
    _info_log = logging.getLogger("log.info")
    _warning_log = logging.getLogger("log.warning")
    _error_log = logging.getLogger("log.error")
    _debug_log = logging.getLogger("log.debug")
    
    
    class ilog(object):
        info = _info_log.info
        debug = _debug_log.debug
        warning = _warning_log.warning
        error = _error_log.error
    

你可能感兴趣的:(#,Python基础,python,开发语言,人工智能,大数据,算法)