Python学习系列 -初探标准库之logging库

系列文章目录

第一章 初始 Python
第二章 认识 Python 变量、类型、运算符
第三章 认识 条件分支、循环结构
第四章 认识 Python的五种数据结构
第五章 认识 Python 函数、模块
第六章 认识面向对象三大特性
第七章 初探标准库之os库
第八章 初探标准库之pathlib库
第九章 初探标准库之Shutil库
第十章 初探标准库之re库
第十一章 初探标准库之time和datetime库
第十二章 初探标准库之configparser和第三方pyyaml库
第十三章 初探标准库之logging库


文章目录

  • 系列文章目录
      • 文章开篇
      • 日志的作用
      • 日志的等级
      • 日志的消息格式
      • logging四大组件
      • Logger类
      • Handler类
      • Formatter类
      • Filter类
      • 真实代码封装
      • 总结


文章开篇

Python的魅力,犹如星河璀璨,无尽无边;人生苦短、我用Python!


日志的作用

日志捕捉是软件运行时追踪内部事件的重要工具,对于错误排查和系统运维都至关重要;
通过在代码中嵌入日志记录方法,开发人员可以捕获并记录难以察觉的事件,并提供事件的背景信息;
此外,日志还标记事件的重要性或严重性级别,帮助开发人员快速识别关键事件,从而提高软件的健壮性和用户体验;


日志的等级


在这里插入图片描述

级别应用场景
DEBUG调试信息,通常在诊断问题的时候用
INFO普通信息,确认程序在按照预期运行
WARNING警告信息,程序运行时出现了意外情况或预示可能出现的问题,但程序仍会继续运行
ERROR错误信息,程序运行中出现了异常错误,程序某些功能无法执行
CRITICAL危险信息,程序运行中出现了严重的错误,导致程序无法继续运行

使用不同的日志级别进行记录日志信息

import logging

# 默认打印到控制台
logging.basicConfig(level=logging.DEBUG)
# 指定记录到日志文件(后续详解)
# logging.basicConfig(filename="test.log", level=logging.DEBUG)

logging.debug("调试信息")       # DEBUG:root:调试信息
logging.info("普通信息")        # INFO:root:普通信息
logging.warning("警告信息")     # WARNING:root:警告信息
logging.error("错误信息")       # ERROR:root:错误信息
logging.critical("危险信息")    # CRITICAL:root:危险信息

日志的消息格式

格式说明
%(message)s日志消息内容
%(asctime)s创建日志记录可读时间
%(funcName)s日志调用所在函数名
%(levelname)s日志级别
%(levelno)s消息级别数字
%(lineno)s日志调用所在源码行号
%(module)s模块(filename名字部分)
%(process)进程ID
%(thread)s线程ID
%(processName)s进程名
%(threadName)s线程名

logging四大组件

在这里插入图片描述

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

Logger类

Logger主要做三件事情:

  • 提供日志记录方法给应用程序;
  • 根据日志的严重等级或过滤器决定处理哪些日志;
  • 将日志消息发送给所有处理器对象;

常用的配置方法说明
Logger.setLevel()为logger对象设置日志器处理的最低日志级别
Logger.addHandler()为logger对象添加一个Handler处理器对象
Logger.removeHandler()为logger对象移除一个Handler处理器对象
Logger.addFilter()为logger对象添加一个Filter过滤器对象
Logger.removeFilter()为logger对象移除一个Filter过滤器对象

关于Logger.setLevel()方法的说明:
在内置的级别中,DEBUG最低,CRITICAL最高
如setLevel(logging.INFO)只会处理INFO及以上级别的日志,而忽略DEBUG级别的消息;

创建日志记录的方法说明
Logger.debug()创建一个调试级别的日志记录
Logger.info()创建一个普通级别的日志记录
Logger.warning()创建一个警告级别的日志记录
Logger.error()创建一个错误级别的日志记录
Logger.critical()创建一个危险级别的日志记录
Logger.exception()创建一个异常级别的日志记录
Logger.log()需要获取一个明确的日志级别参数来创建一个日志记录

获取Logger对象的方式:

  • 通过Logger类的实例化方法创建一个Logger类的实例;
  • 通过logging模块的getLogger()方法获取;

通常我们都是使用第二种方式,logging.getLogger()方法有一个可选参数name
该参数表示将要返回的日志器的名称标识,如果不提供该参数,则其值为root
相同的name参数多次调用getLogger()方法,返回同一个logger对象的引用;

import logging

# 默认打印到控制台
logging.basicConfig(level=logging.DEBUG)

log = logging.getLogger("zhangsan")

log.debug("调试信息")       # DEBUG:root:调试信息
log.info("普通信息")        # INFO:root:普通信息
log.warning("警告信息")     # WARNING:root:警告信息
log.error("错误信息")       # ERROR:root:错误信息
log.critical("危险信息")    # CRITICAL:root:危险信息


Handler类

Handler对象的作用是根据日志消息的级别将其分发到指定位置,如文件、网络或邮件等。
Logger对象可通过addHandler()方法添加多个handler以满足不同的日志需求
将所有严重级别的日志记录到文件;
将严重级别为error及以上的日志输出到标准输出;
将严重级别为critical的日志发送到邮件地址;
上述场景需要多个handlers,每个负责处理特定级别的日志并发送到特定位置


开发人员通常只需关注handler中的少数方法
对于使用内置handler对象的应用开发人员,最关键的是配置方法,如设置日志级别、格式和输出目标等;
其他方法对于大多数应用场景来说可能并不重要;


Handler常用配置方法说明
Handler.setLevel()为handler对象设置最低严重级别的日志消息
Handler.setFormatter()为handler对象设置一个Formatter格式器对象
Handler.addFilter()为handler对象添加一个Filter过滤器对象
Handler.removeFilter()为handler对象移除一个Filter过滤器对象

因为Handler是基类,仅定义了通用接口和默认行为;
应用程序代码应避免直接实例化和使用Handler实例;
开发人员应使用其子类或覆盖默认行为来满足具体需求;


常用的Handler对象说明
logging.StramHandler将日志消息输出到Stream,如std.out、std.err或任何file-like对象
logging.FileHandler将日志消息发送到磁盘文件,默认情况下文件会无限增长
logging.handlers.RotaingFileHandler将日志消息发送到磁盘文件,并支持日志文件按大小切割
logging.hanlders.TimedRotatingFileHandler将日志消息发送到磁盘文件,并支持日志文件按时间切割
logging.handlers.HTTPHandler将日志消息以GET或POST的方式发送给HTTP服务器
logging.handlers.SMTPHandler将日志消息发送给指定的Email地址
logging.NullHandler该Handler实例会忽略error messages,通常被想使用logging的library开发者使用来避免’No handlers could be found for logger XXX’信息的出现。

import logging

# 创建一个logger
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)  # 设置日志级别

# 创建一个handler,用于将日志信息打印到控制台
handler = logging.StreamHandler()

# 给logger添加handler
logger.addHandler(handler)

# 记录一些日志
logger.debug('这是一个调试信息...')     # 这是一个调试信息...
logger.info('这是一个普通信息...')      # 这是一个普通信息...
logger.warning('这是一个警告信息...')   # 这是一个警告信息...
logger.error('这是一个错误信息...')     # 这是一个错误信息...
logger.critical('这是一个危险信息...')  # 这是一个危险信息...



Formatter类

Formater对象用于配置日志信息的最终顺序、结构和内容
与logging.Handler基类不同的是,Formatter类可以直接实例化
对于特殊的输出需求,可以根据需求创建Formatter的子类来实现;

Formatter类的构造方法定义如下:

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

该构造方法有三个可选参数:

  • fmt用于定义消息格式,默认为原始消息;
  • datefmt设置日期格式,默认是"%Y-%m-%d %H:%M:%S";
  • style在Python 3.2引入,可选’%', ‘{‘或’$’,默认为 ‘%’;

import logging

# 创建一个logger
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)  # 设置日志级别

# 创建一个handler,用于将日志信息打印到控制台
handler = logging.StreamHandler()

# 定义handler的输出格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

# 给logger添加handler
logger.addHandler(handler)

# 记录一些日志
logger.debug('这是一个调试信息...')     # 2024-02-27 14:11:17,513 - my_logger - DEBUG - 这是一个调试信息...
logger.info('这是一个普通信息...')      # 2024-02-27 14:11:17,513 - my_logger - INFO - 这是一个普通信息...
logger.warning('这是一个警告信息...')   # 2024-02-27 14:11:17,513 - my_logger - WARNING - 这是一个警告信息...
logger.error('这是一个错误信息...')     # 2024-02-27 14:11:17,513 - my_logger - ERROR - 这是一个错误信息...
logger.critical('这是一个危险信息...')  # 2024-02-27 14:11:17,513 - my_logger - CRITICAL - 这是一个危险信息...



Filter类

Filter是logging模块中一个基类
用于Handler和Logger实现比日志级别更细粒度和复杂的过滤功能,它允许根据logger的层级来筛选日志事件;
filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,返回值为非0表示可以通过过滤


class logging.Filter(name='')
    filter(record)

Filter类根据传入的name参数值来过滤日志记录
name设置为’A.B’时,它只允许logger名称以’A.B’为前缀(包括’A.B’本身和它的任何子logger)的日志记录通过过滤;
对于不符合这一前缀规则的logger名称,如’A.BB’和’B.A.B’,它们产生的日志记录将被过滤掉
如果name参数值为空字符串,则该过滤器允许所有日志事件通过,不进行任何名称相关的过滤

import logging


class MyFilter(logging.Filter):
    def __init__(self, name='', key_word='测试'):
        super().__init__(name)
        # 设置filter的名字,虽然这里使用了,但实际上filter的名字通常不用于过滤决策
        self.name = name
        # 设置过滤条件
        self.key_word = key_word
        self.levelno = logging.INFO
        self.logger_name = 'my_app'

    def filter(self, record):
        # 如果日志信息中以“测试”开头,则忽略
        if record.getMessage().startswith(self.key_word):
            return False
        # 如果record的levelno和logger_name与设置的条件匹配,则返回True,否则返回False
        return record.levelno == self.levelno and record.name == self.logger_name


# 创建一个logger
logger = logging.getLogger('my_app')
logger.setLevel(logging.INFO)

# 创建一个handler,用于写入日志文件
handler = logging.StreamHandler()

# 创建一个自定义的filter
filter_instance = MyFilter()

# 添加filter到handler
handler.addFilter(filter_instance)

# 定义handler的输出格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

# 给logger添加handler
logger.addHandler(handler)

# 上述过滤器对象,设置仅记录INFO等级
logger.info('这是一个普通信息...')          # 2024-02-27 14:11:17,513 - my_logger - INFO - 这是一个普通信息...
logger.info('测试是否能够捕捉info信息...')   # 不会被记录

真实代码封装

真实应用案例封装,可直接拿去运用到实际开发项目中;
Log类是通过Python的内置模块logging模块封装的工具类;
代码释义如下:

  • 单例模式,保证其他模块导入时不会重新实例化
  • 按天生成,每天生成一个日志文件,不会让日志过大或过小
  • 格式美化,展示日期时间、模块名称、行号、日志等级
  • 输出打印
  • 文件记录

import logging
import datetime
import os
import time
import traceback  # 回溯模块


# 通过装饰器完成单例模式
def singleton(cls):
    # 使用字典存储类对象的实例
    instances = {}

    def _singleton(*args, **kwargs):
        if cls not in instances:
            # 如果类没有被创建过,那就new个新对象并存储到字典中
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return _singleton


class Log(object):

    def __init__(self):
        '''创建日志器'''
        self.log = logging.getLogger()
        # 创建handlers之前,将已经存在的移除,再重新创建,可以防止日志重复
        while self.log.hasHandlers():
            for handler in self.log.handlers:
                self.log.removeHandler(handler)
        # 设置日志信息等级
        self.log.setLevel(level='INFO')

    def console_handle(self):
        '''创建控制台处理器'''
        self.console_handler = logging.StreamHandler()
        # 设置格式器
        self.console_handler.setFormatter(self.get_formatter()[0])
        # 返回作用给日志器使用
        return self.console_handler

    def file_handle(self):
        '''创建文件处理器'''
        # 当前文件的绝对路径去除当前文件所在目录加上日志文件存放目录
        logfile_path = os.path.dirname(os.path.dirname(__file__)) + "/logs"
        current_date = datetime.datetime.now().strftime("%Y-%m-%d")
        logfile_name = logfile_path + '/执行日志 ' + current_date + '.log'
        try:
            # 判断是否已经生成日志文件存放目录
            if not os.path.exists(logfile_path):
                os.makedirs(logfile_path)
            # 判断是否已经生成今天的日志文件
            if not os.path.exists(logfile_name):
                open(logfile_name, mode='a')
        except OSError:
            pass
        self.file_handler = logging.FileHandler(filename=logfile_name, mode='a', encoding='utf-8')
        # 设置格式器
        self.file_handler.setFormatter(self.get_formatter()[1])
        # 返回作用给日志器使用
        return self.file_handler

    def get_formatter(self):
        '''格式器'''
        self.console_fmt = logging.Formatter(
            fmt='%(asctime)s --> %(filename)-30s [line:%(lineno)-3d] --> %(levelname)-5s --> %(message)s')
        self.file_fmt = logging.Formatter(
            fmt='%(asctime)s --> %(filename)-30s [line:%(lineno)-3d] --> %(levelname)-5s --> %(message)s')
        # 返回作用给控制台处理器、文件处理器使用
        return self.console_fmt, self.file_fmt

    @singleton
    def get_log(self):
        '''日志器添加处理器'''
        self.log.addHandler(self.console_handle())
        self.log.addHandler(self.file_handle())
        return self.log


log = Log().get_log()

if __name__ == '__main__':
    log = Log().get_log()
    log.info('提示信息')
    log.error('错误信息')
    try:
        int('hello world')
    except ValueError as e:
        print('在此处进行异常的处理')
        # 将错误信息写入日志文件
        log.error(traceback.format_exc())

    time.sleep(3)
    print('测试是否被异常中断')


# 日志文件内容如下:
# 2024-02-27 14:40:10,923 --> LoggerCfg.py  [line:84 ] --> INFO  --> 提示信息
# 2024-02-27 14:40:10,923 --> LoggerCfg.py  [line:85 ] --> ERROR --> 错误信息
# 2024-02-27 14:40:10,923 --> LoggerCfg.py  [line:91 ] --> ERROR --> Traceback (most recent call last):
#  File "/Users/yangkai/PythonWorkspace/第49课:第三方库/CASE11:Logging/LoggerCfg.py", line 87, in <module>
#    int('hello world')
# ValueError: invalid literal for int() with base 10: 'hello world'


总结

Python的logging模块是Python标准库中的一个强大的日志记录工具;
它支持多种日志级别、灵活的配置选项和丰富的输出目标,如文件、控制台等;
日志记录是记录应用运行时状态和事件的关键方法,对于问题诊断、流程追踪、错误发现及性能优化具有重要意义;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/414854.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构(C语言)】排序详解

目录 文章目录 前言 一、排序的概念 1.1 排序的概念 1.2 常见的排序算法 二、插入排序 2.1 直接插入排序 2.1.1 基本思想 2.1.2 特性总结 2.1.3 代码实现 2.2 希尔排序 2.2.1 基本思想 2.2.2 特性总结 2.2.3 代码实现 三、选择排序 3.1 直接选择排序 3.1.1…

【数据结构和算法初阶(C语言)】空间复杂度(例题剖析一起探究空间如何评价算法)

目录 1.衔接前言-时间复杂度的回顾 2.关于算法复杂度 3.本文主角-空间复杂度 3.1大O的渐进表示方法 4.空间复杂度例题----实际感受空间复杂度 4.1冒泡排序的空间复杂度 4.2计算递归函数的空间复杂度 4.3动态开辟内存版本求斐波那契数列的空间复杂度 4.4&#xff08;…

蓝桥杯_定时器的基本原理与应用

一 什么是定时器 定时器/计数器是一种能够对内部时钟信号或外部输入信号进行计数&#xff0c;当计数值达到设定要求时&#xff0c;向cpu提出中断处理请求&#xff0c;从而实现&#xff0c;定时或者计数功能的外设。 二 51单片机的定时/计数器 单片机外部晶振12MHZ&#xff0c;…

如何实现双向循环链表

博主主页&#xff1a;17_Kevin-CSDN博客 收录专栏&#xff1a;《数据结构》 引言 双向带头循环链表是一种常见的数据结构&#xff0c;它具有双向遍历的特性&#xff0c;并且在表头和表尾之间形成一个循环。本文将深入探讨双向带头循环链表的结构、操作和应用场景&#xff0c;帮…

el-table通过这样封装可以实现校验-表格校验的原理

我们一般在后台系统中&#xff0c;很常见的操作时表格里面嵌套表单&#xff0c;之前我的网上找到了一些封装的用法&#xff1a; <el-form :model"formData" :rules"ruleData" ref"formDom"><el-table :data"formData.tableData&q…

Vue.js入门指南:简介、环境配置与Yarn创建项目

一、Vue.js简介 Vue.js&#xff0c;一个流行的JavaScript框架&#xff0c;以其直观、灵活和高效的特点&#xff0c;在前端开发者中赢得了广泛的赞誉。Vue.js的核心库专注于视图层&#xff0c;使得开发者能够构建出响应式的数据绑定和组合的视图组件。Vue.js的目标是通过尽可能简…

CPU、GPU 混合推理,非常见大模型量化方案:“二三五六” 位量化,模型量化详细实现方案

CPU、GPU 混合推理&#xff0c;非常见大模型量化方案&#xff1a;“二三五六” 位量化&#xff0c;模型量化详细实现方案。 非常见整型位数的量化&#xff0c;来自让各种开源模型能够在 CPU 环境、CPU & GPU 环境混合推理的技术方案&#xff1a;llama.cpp 。为了能够在低配…

iOS群控软件功能分析与代码分享!

随着移动互联网的迅猛发展&#xff0c;iOS设备作为市场上一大主流平台&#xff0c;其应用开发和管理越来越受到开发者和企业的重视&#xff0c;iOS群控软件&#xff0c;作为一种能够批量控制、管理和监控iOS设备的工具&#xff0c;逐渐展现出其强大的实用价值。 本文将详细分析…

React回顾

一、基础 1、使用babel解析 2、不直接使用jsx&#xff0c;jsx写起来很繁琐 3、jsx语法规则 4、函数式组件的使用 5、函数式组件渲染 6、类组件渲染 7、类组件中事件调用this指向问题 8、类组件不能直接改变状态 9、props接收数据类型限制 类型限制放到类组件内部&#xff0c;用…

StarRocks实战——滴滴OLAP的技术实践与发展方向

原文大佬的这篇StarRocks实践文章整体写的很深入&#xff0c;介绍了StarRocks数仓架构设计、物化视图加速实时看板、全局字典精确去重等内容&#xff0c;这里直接摘抄下来用作学习和知识沉淀。 目录 一、背景介绍 1.1 滴滴OLAP的发展历程 1.2 OLAP引擎存在的痛点 1.2.1 运维…

IOC 和 AOP

IOC 所谓的IOC&#xff08;inversion of control&#xff09;&#xff0c;就是控制反转的意思。何为控制反转&#xff1f; 在传统的程序设计中&#xff0c;应用程序代码通常控制着对象的创建和管理。例如&#xff0c;一个对象需要依赖其他对象&#xff0c;那么它会直接new出来…

express+mysql+vue,从零搭建一个商城管理系统6--数据校验和登录

提示&#xff1a;学习express&#xff0c;搭建管理系统 文章目录 前言一、修改models/user.js二、修改routes下的user.js三、Api新建user/login接口四、删除数据库原有数据&#xff0c;添加新验证规则的用户四、用户登录总结 前言 需求&#xff1a;主要学习express&#xff0c;…

【Linux】基础篇-Linux四种环境搭建的方式(详细安装说明步骤,搭载下载安装地址)

目录 1. 使用虚拟机&#xff08;推荐VMware&#xff09;centos 7版本 1.1VMware虚拟机下载 1.2VMware 安装 1.3centos-7 清华大学镜像下载 1.4 centos-7 清华大学镜像导入虚拟机VMware 2.使用虚拟机ubuntu 20.04版本 2.1虚拟机下载同上 2.2虚拟机安装同上 2.3ubunt…

ROS-Ubuntu 版本相关

ROS-Ubuntu 版本相关&#xff1a;安装指引 年代ROS1版本Ubuntu 版本2014Indigo14.042016Kinetic16.042018Melodic18.042020Noetic20.04 & 22.04 ROS2兼顾了工业使用上的问题。 年代ROS2版本Ubuntu 版本2022Humble20.04 & 22.042023Iron16.04 相关参考&#xff1a; […

【Qt 学习之路】使用 cmake 在Windows上 编译 ZeroMQ

文章目录 1、概述2、编译2.1、用 Visual Studio 的解决方案方式2.1.1、找到 Builds 文件夹2.1.2、查看 deprecated-msvc 下的 libzmq.sln 文件2.1.3、使用 Visual Studio 打开 libzmq.sln 解决方案2.1.4、修改 libzmq.import.props 文件2.1.5、编译生成 2.2、用 C 的cmake方式2…

【前端入门】设计模式+单多页+React

设计模式是一种解决特定问题的经验总结&#xff0c;它提供了经过验证的解决方案&#xff0c;可以在软件开发过程中使用。设计模式可以帮助前端开发人员更有效地组织和管理代码&#xff0c;并提供一种共享的语言和框架&#xff0c;以便与其他开发人员进行交流。 以下是一些常见…

十二、Qt自定义Widget组件、静态库与动态库

一、自定义Widget组件 1、自定义Widget组件 使用步骤采用提升法&#xff08;promotion&#xff09;重新定义paintEvent事件 2、实现程序 &#xff08;1&#xff09;创建项目&#xff0c;基于QWidget &#xff08;2&#xff09;添加类&#xff0c;为Widget组件提升类 #inclu…

Delegate(P29 5.5delegate)

一、Delegate简介 每个代理都可以访问许多附加的属性&#xff0c;其中一些来自数据模型&#xff0c;另一些来自视图。 从模型中&#xff08;Model&#xff09;&#xff1a;属性将每个项目的数据传递给 delegate。 从视图中&#xff08;View&#xff09;&#xff1a;属性将状…

dcat admin 自定义页面

自定义用户详情页 整体分为两部分&#xff1a;用户信息、tab框 用户信息采用自定义页面加载&#xff0c;controller代码如下&#xff1a; protected function detail($id) {return Show::make($id, GameUser::with(finance), function (Show $show) {// 这段就是加载自定义页面…

pdf怎么合并在一起?

pdf怎么合并在一起&#xff1f;在日常工作和学习中&#xff0c;我们常常需要处理大量的PDF文件。有时候&#xff0c;我们可能希望将多个PDF文件合并成一个文件&#xff0c;以便于管理和分享。这时候&#xff0c;PDF文件合并工具就能派上用场了。PDF文件合并是一种将多个PDF文件…
最新文章