【Python】Logging模块调试详解与实用代码示例

文章目录

  • 1. logging基本使用
  • 2. logging的高级应用
  • 3. logging初始化封装示例
  • 1. logging基本使用

    logging日志级别:

    级别 数值 使用场景示例 生产环境建议
    DEBUG 10 变量跟踪、算法中间状态 关闭
    INFO 20 服务启动、关键业务流程节点 保留
    WARNING 30 低磁盘空间、非预期参数但可处理 保留
    ERROR 40 数据库连接失败、文件解析异常 保留
    CRITICAL 50 系统崩溃、核心服务不可用 保留

    默认日志级别warning

    使用logging向控制台输出日志代码示例:

    import logging
    
    # 默认日志输出级别warning
    # 指定输出级别basicConfig
    
    logging.basicConfig(level=logging.DEBUG)
    
    # 能保证logging输出顺序一致,但是能保证logging与print输出顺序一致
    # 可能会出现先输出logging,再输出print的情况, 内部日志输出机制不是同步的, 异步写入, 支持高并发
    
    print("this is print log")
    
    logging.debug("This is Debug log")
    logging.info("This is info log")
    logging.warning("This is warning log")
    logging.error("This is error log")
    logging.critical("This is citical log")
    

    logging将日志输出到文件:

    import logging
    
    # 默认日志输出级别warning
    # 指定输出日志文件、日志记录级别 basicConfig
    # 如果没有会新建demo.log文件,若有则会在文件后追加log日志
    # filemode = "w"先清空再写入, "a"是追加写入, 默认是追加写入
    
    logging.basicConfig(filename='demo.log',  filemode="w", encoding="utf-8", level=logging.DEBUG)
    
    logging.debug("This is Debug log")
    logging.info("This is info log")
    logging.warning("This is warning log")
    logging.error("This is error log")
    logging.critical("This is citical log")
    

    logging输出变量:

    import logging
    
    # 向日志输出变量
    logging.basicConfig(level=logging.DEBUG)
    
    name = "张三"
    age = 18
    
    logging.debug("This is Debug log")
    logging.debug("姓名 %s, 年龄 %d", name, age)
    logging.debug("姓名 %s, 年龄 %d" % (name, age))
    logging.debug("姓名 {}, 年龄{}".format(name, age))
    logging.debug(f"姓名 {name}, 年龄{age}")
    
    # 输出
    # DEBUG:root:This is Debug log
    # DEBUG:root:姓名 张三, 年龄 18
    # DEBUG:root:姓名 张三, 年龄 18
    # DEBUG:root:姓名 张三, 年龄18
    # DEBUG:root:姓名 张三, 年龄18
    

    输出日期:

    import logging
    
    # 输出格式和添加一些公共变量, 时间,日志级别,所在行数,信息
    # datafmt 设置时间格式
    
    logging.basicConfig(format="%(asctime)s | %(levelname)s | %(filename)s:%(lineno)s | %(message)s",
                        datefmt="%Y-%m-%d %H:%M:%S",
                        level=logging.DEBUG)
    
    name = "张三"
    age = 18
    logging.debug("姓名 %s, 年龄 %d", name, age)
    # 输出:
    # 2025-03-13 08:10:33 | DEBUG | mylog.py:11 | 姓名 张三, 年龄 18
    

    2. logging的高级应用

    logging模块采用了模块化设计,主要包含四种组件:

  • Loggers:记录器,提供应用程序代码能直接使用的接口
  • Handlers:处理器,将记录器产生的日志发送至目的地
  • Filters:过滤器,提供更好的粒度控制,决定哪些日志会被输出
  • Formatters:格式化器,设置日志内容的组成结构和消息字段
  • 一个记录器可以对应多个处理器

    loggers记录器:

    1. 提供应用程序的调用接口
      logger = logging.getLogger(__name__)

    2. 决定日志记录的级别
      logger.setLevel()

    3. 将日志内容传递到相关联的handlers中:
      logger.addHandler() 和 logger.removeHandler()

    只要 __name__不变,logger就是单例的

    Handlers处理器:

    他们将日志分发到不同的目的地。可以是文件、标准输出、邮件、或者socket、http等协议发送到任何地方

    StreamHandler:

  • 标准输出 stdout 分发器
  • 创建方法:sh=logging.StreamHandler(stream=None)
  • FileHandler:

  • 将日志保存到磁盘文件的处理器
  • 创建方法: fh = logging.FileHandler(filename, mode=“a”, encoding=None, delay=False)
  • setFormatter() : 设置当前 handler 对象使用的消息格式
  • 其它Handdlers处理器:

    FileHandler: 只会往一个文件中写,需要人为备份
    RotatingFileHandler: 按照文件大小分别写入日志
    TimedRotatingFileHandler:按照时间写入日志

    Formatters格式:

  • Formatters对象用来最终设置日志信息的顺序、结构和内容

  • 其构造方法为:ft = logging.Formatter.__init__(fmt=None, datefmt=None, style=’ %')

  • datefmt 默认是 %Y-%m-%d %H:%M:%S样式的

  • style 参数默认为百分符 %, 这表示 %(<dictionary key>)s 格式的字符串

  • Python代码示例:

    import logging
    
    # 记录器
    logger = logging.getLogger('applog')
    logger.setLevel(logging.DEBUG) 
    
    # 处理器handler
    # 向终端输出
    consoleHandler = logging.StreamHandler()
    consoleHandler.setLevel(logging.INFO)
    
    # 向文件输出,没有设置日志级别,将使用logger的级别
    fileHnadler = logging.FileHandler(filename="addDemo.log")
    fileHnadler.setLevel(logging.INFO)
    
    # Formatter格式
    formatter = logging.Formatter("%(asctime)s|%(levelname)8s|%(filename)s:%(lineno)4s|%(message)s")
    
    # 给处理器设置格式
    consoleHandler.setFormatter(formatter)
    fileHnadler.setFormatter(formatter)
    
    # 记录器设置处理器,会向控制台和 文件同时写入日志
    logger.addHandler(consoleHandler)
    logger.addHandler(fileHnadler)
    
    # 定义一个过滤器
    flt = logging.Filter("cn.cccb") # logger现在的名字是 app.log, 不满足以cn.cccb开头,所以没有日志输出
    
    # 关联过滤器
    # logger.addFilter(flt)     # 记录器关联过滤器
    fileHnadler.addFilter(flt)  # 处理器关联过滤器
    
    # 打印日志代码
    logger.debug("This is debug log")
    logger.info("This is info log")
    logger.warning("This is warning log")
    logger.error("This is error log")
    logger.critical("This is critical log")
    

    使用配置文件的形式:

    测试代码:

    import logging
    import logging.config
    
    # 配置文件方式来处理日志
    # 记录器
    
    logging.config.fileConfig('logging.conf')
    
    rootLogger = logging.getLogger()
    rootLogger.debug("this is root Logger, debug")
    
    logger = logging.getLogger('applog')
    logger.debug("This is applog Logger , debug")
    
    
    a = 'abc'
    try:
        int(a)
    except Exception as e:
        logger.exception(e)     # 将报错输出到日志
    
    # 1. 代码形式
    # 2. 配置文件形式
    # 3. 字典形式
    

    logging.conf :

    [loggers]
    keys = root,applog    
    
    [handlers]
    keys = fileHandler, consoleHandler
    
    [formatters]
    keys = simpleFormatter
    
    [logger_root]
    level = DEBUG
    handlers = consoleHandler
    
    [logger_applog]
    level = DEBUG
    handlers = fileHandler,consoleHandler
    qualname = applog       
    propagate = 0           
    
    [handler_consoleHandler]
    class = StreamHandler
    args = (sys.stdout,)
    level = DEBUG
    formatter = simpleFormatter
    
    [handler_fileHandler]
    class = logging.handlers.TimedRotatingFileHandler
    # 参数说明:filename, when, interval, backupCount
    args = ('applog.log', 'midnight',1,0)  
    level = DEBUG
    formatter = simpleFormatter
    
    [formatter_simpleFormatter]
    format = %(asctime)s|%(levelname)8s|%(filename)s:%(lineno)4d|%(message)s
    datefmt = %Y-%m-%d %H:%M:%S
    

    本文参考: https://www.bilibili.com/video/BV1sK4y1x7e1/?spm_id_from=333.1387.favlist.content.click&vd_source=cf0b4c9c919d381324e8f3466e714d7a

    3. logging初始化封装示例

    import os
    import logging
    
    # log 日志相关设置
    class LoggerConfig(Enum):
        LOG_NAME = "WPF"                            
        LOG_DIR = "logs"
        LOGGER_FORMATTER = "%(asctime)s|%(levelname)8s|%(filename)s:%(lineno)4s|%(message)s"
        LOGGER_LEVEL = logging.DEBUG            # logger日志级别
        LOGGER_MODE = 0                         # 0:控制台和文件日志输出  1:控制台日志输出  2: 文件日志输出
    
    def setup_logging(
            name = LoggerConfig.LOG_NAME.value,
            log_dir = LoggerConfig.LOG_DIR.value,
            logger_formatter = LoggerConfig.LOGGER_FORMATTER.value,
            level = LoggerConfig.LOGGER_LEVEL.value,
            mode = LoggerConfig.LOGGER_MODE.value
        ):
        """
        初始化日志
        Args:
            name: log 名称
            log_dir: 日志存放目录
            logger_formatter: 日志格式
            level: 日志级别
            mode: 0、1、2   0:控制台和文件日志输出  1:控制台日志输出  2: 文件日志输出
        """
        # 如果日志目录不存在则创建
        try:
            os.makedirs(log_dir, exist_ok=True)
        except Exception as e:
            raise RuntimeError(f"创建日志目录失败: {log_dir}") from e
        
        
        # 记录器
        logger = logging.getLogger(name)
        logger.setLevel(level)
    
        # 格式    
        formatter = logging.Formatter(logger_formatter)
        
        # 终端输出处理器
        consoleHandler = logging.StreamHandler()
        consoleHandler.setLevel(level)
        consoleHandler.setFormatter(formatter)
        
        # 文件输出处理器
        log_file = os.path.join(log_dir, f"{name}.log")
        fileHandle = logging.FileHandler(log_file)
        fileHandle.setLevel(level)
        fileHandle.setFormatter(formatter)
        
        # 模式
        if mode == 0:
            logger.addHandler(consoleHandler)
            logger.addHandler(fileHandle)
        
        elif mode ==1:
            logger.addHandler(consoleHandler)
            
        elif mode==2:
            logger.addHandler(fileHandle)
            
        else:
            raise ValueError(f"无效的模式参数: {mode},可选值 0/1/2")
        
        return logger
    
    
    logger = setup_logging()
    
    if __name__ == "__main__":
        pass
    
    
    

    作者:小小小小祥

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【Python】Logging模块调试详解与实用代码示例

    发表回复