当前位置: 首页 > news >正文

Python logging 模块详解

pkYsjL8.jpg

Python 的 logging 模块提供了一个强大而灵活的日志系统。它是 Python 标准库的一部分,因此可以在任何 Python 程序中使用。logging 模块提供了许多有用的功能,包括日志消息的级别设置、日志消息的格式设置、将日志消息输出到不同的目标,以及处理复杂的日志系统配置。

logging 组成部分

在 logging 模块中主要有四部分:Logger、Handler、Filter 和 Formatter。下面分别对这四部分进行介绍。

Logger

Logger 就是程序可以直接调用的一个日志接口,可以直接向 Logger 写入日志信息。但是 Logger 并不是直接实例化使用的,而是通过 logging.getLogger(name) 来获取对象,事实上 Logger 对象是单例模式,并且 logging 是多线程安全的,也就是在程序的任何地方使用的同名的 Logger 都指向了一个实例。

Handler

Handler 作用于 Logger,是真正的日志处理程序,一个 Logger 可以配置多个 Handler。同样的,一个 Handler 也可以作用在多个 Logger 上。每个 Handler 同样有一个日志级别,也就是说 Logger 可以根据不同的日志级别将日志传递给不同的 Handler,当然也可以相同的级别传递给多个 Handler 这就根据需求来灵活的设置了。

Handler 既有系统定义的,比如将日志输出到标准输出 stdout。也可以自定义日志处理器,比如将日志发送到邮件或者第三方的日志存储介质等。

Filter

Filter 提供了更细粒度的判断,来决定日志是否需要打印。原则上 Handler 获得一个日志就必定会根据日志级别进行统一处理,但是如果 Handler 拥有一个 Filter 的时候就可以对日志进行额外的处理和判断。例如 Filter 能够对来自特定源的日志进行拦截甚至进行修改(包括日志级别的修改,修改后再进行级别判断)。

Logger 和 Handler 都可以安装 Filter 甚至可以安装多个 Filter 串联起来。

Formatter

Formatter 指定了最终某条记录打印的格式。Formatter 会将传递来的信息拼接成一条具体的字符串,默认情况下 Formatter 只会将信息 %(message)s 直接打印出来。Formatter 中有一些自带的属性可以使用,如下表格:

pkYsOQP.png

注意的是一个 Handler 只能拥有一个 Formatter。

日志级别

作用

日志级别是一个用于控制和过滤日志消息的重要工具。日志级别可以帮助更有效地管理日志,只看到需要的信息,而忽略不必要的细节。

这个功能在开发和运行大型程序时非常有用。例如,在开发过程中,你可能需要 DEBUG 级别的日志来找出问题。但是在生产环境中,这么详细的日志可能会占用大量的磁盘空间,而且大部分信息可能都是不必要的。因此,在生产环境中,你可能只需要 WARNING 或者 ERROR 级别的日志。

级别

在记录日志时, 日志消息都会关联一个级别(“级别”本质上是一个非负整数)。系统默认提供了6个级别,它们分别是:

级别对应值
CRITICAL50
ERROR40
WARNING30
INFO20
DEBUG10
NOTSET0

继承关系和处理流程

Logger 继承关系

Logger 对象是有父子关系的,当没有父 Logger 对象时它的父对象是 root,当拥有父对象时父子关系会被修正。举个例子,logging.getLogger("abc.xyz") 会创建两个 Logger 对象,一个是 abc 父对象,一个是 xyz 子对象,同时 abc 没有父对象,所以它的父对象是 root。但是实际上 abc 是一个占位对象(虚的日志对象),可以没有 Handler 来处理日志。但是 root 不是占位对象,如果某一个日志对象打日志时,它的父对象会同时收到日志,所以有些使用者发现创建了一个 Logger 对象时会打两遍日志,就是因为他创建的 Logger 打了一遍日志,同时 root 对象也打了一遍日志。

1)level的继承

子 Logger 写日志时,优先使用本身设置了的 level;如果没有设置,则逐层向上级父 Logger 查询,直到查询到为止。最极端的情况是,使用 root Logger 的默认日志级别 logging.WARNING。

参考源码:

def getEffectiveLevel(self):"""Get the effective level for this logger.Loop through this logger and its parents in the logger hierarchy,looking for a non-zero logging level. Return the first one found."""logger = selfwhile logger:if logger.level:return logger.levellogger = logger.parentreturn NOTSET

2)Handler 的继承

先将日志对象传递给子 Logger 的所有 Handler 处理,处理完毕后,如果该子 Logger 的 propagate 属性没有设置为 False,则将日志对象向上传递给第一个父 Logger,该父 Logger 的所有 Handler 处理完毕后,如果它的 propagate 也没有设置为 False,则继续向上层传递,以此类推。最终的状态,要么遇到一个 Logger,它的 propagate 属性设置为了 False;要么一直传递直到 root Logger 处理完毕。

注意,Handler 不是真正的(类)继承,只是“行为上的继承”,也就是子 Logger 并没有没有绑定父类的 Handler。

参考源码:

def callHandlers(self, record):"""Pass a record to all relevant handlers.Loop through all handlers for this logger and its parents in thelogger hierarchy. If no handler was found, output a one-off errormessage to sys.stderr. Stop searching up the hierarchy whenever alogger with the "propagate" attribute set to zero is found - thatwill be the last logger whose handlers are called."""c = selffound = 0while c:for hdlr in c.handlers:found = found + 1if record.levelno >= hdlr.level:hdlr.handle(record)if not c.propagate:c = None  # break outelse:c = c.parentif (found == 0) and raiseExceptions and not self.manager.emittedNoHandlerWarning:sys.stderr.write("No handlers could be found for logger"" \"%s\"\n" % self.name)self.manager.emittedNoHandlerWarning = 1
日志记录的处理流程

pkYsXsf.png

在一个给定继承关系的日志配置中,如果其中任何一个 Logger 进行了日志记录,会经历下面几个步骤:

  • 首先这条记录会被 Logger 本身的 Handler 进行处理。
    • 检查日志级别是否满足最低要求
    • 是否有 Filter 进行更细粒度的处理
    • 用什么样的 Formatter 作为打印格式
  • 默认 Logger 的 propagate 值为 True,也就是在自己进行日志处理的同时,也会把这条日志代理到其父 Logger 进行处理,一直会被代理到 root Logger。

参考下面代码:

import logging# root logger
logging.basicConfig(level=logging.INFO)# parent logger
logger_parent = logging.getLogger("parent")
handler = logging.StreamHandler()
handler.setLevel(logging.CRITICAL)  # Set the handler's level to CRITICAL
logger_parent.addHandler(handler)
logger_parent.setLevel(level=logging.CRITICAL)# child logger
logger_child = logging.getLogger("parent.child")
logger_child.addHandler(logging.StreamHandler())
logger_child.setLevel(level=logging.INFO)if __name__ == "__main__":logger_child.info("Hi")

其输出结果为:

Hi
INFO:parent.child:Hi

在上面代码中有三个日志记录器,其继承关系分别是 logger_child 继承 logger_parent 继承 root。

每个日志记录器上设置的日志级别,比如代码 logger_parent.setLevel(level=logging.CRITICAL) 不会影响记录器对日志的代理关系,其影响的是这个记录器在使用时是否处理程序中的日志记录。比如 logger_parent 在使用的时候会忽略 CRITICAL 级别之下的日志。

在上述程序中:

  • Hi 这条日志首先会被 logger_child 的 Handler 进行处理。
  • 然后这条记录会被代理到 logger_parent,logger_parent 的 handler 会对这条日志做处理,由于 logger_parent 的 Handler 是 CRITICAL 级别的,因此忽略这条日志。
  • 接下来这条日志会继续被代理到 root,root 的 Handler 接下来会继续处理这条日志。

因此日志是否会被向上代理,取决于 Logger 上的 propagate 属性。

使用实例

基本日志打印

在下面的例子中,默认的日志级别是 WRANING,因此只有最后一个日志被打印出来。

import logginglogging.debug('this is debug message')
logging.info('this is info message')
logging.warning('this is warning message')# 打印结果:WARNING:root:this is warning message
root 日志配置

在下面的例子中,root 日志记录器可以通过 logging.basicConfig 进行配置,比如配置其默认的日志级别,Handler 或者输出格式等。

import logginglogging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(message)s')if __name__ == '__main__':logging.debug('this is debug message')logging.info('this is info message')logging.warning('this is warning message')'''''
结果:
2024-06-06 17:10:43,873 - root - this is debug message
2024-06-06 17:10:43,873 - root - this is info message
2024-06-06 17:10:43,873 - root - this is warning message
'''
将日志同时输出到文件和 stdout

在下面的配置中,定义了一个文件 Handler 和标准输出 Handler,并将其应用到 logger 日志处理器上,这样日志会同时被输出到文件和标准输出中。

如果你用 FileHandler 写日志,文件的大小会随着时间推移而不断增大。为了避免这种情况出现,可以在你的生成环境中使用 RotatingFileHandler 替代 FileHandler 做日志回滚。

import logginglogger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)# 文件 Handler
file_handler = logging.FileHandler("log.txt")
file_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)# 标准输出 Handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)# 在 logger 中应用这两个 Handler
logger.addHandler(file_handler)
logger.addHandler(console_handler)logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")
使用适配器扩展

在下面的例子中通过 logging 提供的 LoggerAdapter 方法可以对日志的字段进行扩展,输出的时候通过适当的 Formatter 进行扩展字段的输出。

import loggingformat_str = '%(levelname)s %(filename)s %(document_id)s %(message)s'
logging.basicConfig(level=logging.INFO, format=format_str)logger = logging.getLogger(__name__)extra = {'document_id': 'doc id'}
logger = logging.LoggerAdapter(logger, extra)logger.info({'name': 'Xiao Ming'})# INFO example_use_adapter.py doc id {'name': 'Xiao Ming'}
捕捉异常并使用 traceback 记录

当程序出现错误的时候,在使用 Logger 进行记录的时候通过设置参数 exc_info=True 可以在日志中记录详细的报错信息。

import logginglogger = logging.getLogger(__name__)try:open('/path/to/does/not/exist', 'rb')
except Exception as e:logger.error('Failed to open file', exc_info=True)'''
Failed to open file
Traceback (most recent call last):File "/Users/crown/Projects/python101/playground/logging_watchtower/example_traceback.py", line 6, in <module>open('/path/to/does/not/exist', 'rb')
FileNotFoundError: [Errno 2] No such file or directory: '/path/to/does/not/exist'
'''
通过配置文件

如果日志的配置比较复杂,可以考虑通过外置配置文件的方式进行 logging 配置,logging 模块支持 json 格式和 yaml 两种格式的配置文件,通过配置文件可以更清晰的进行负责日志的配置。

下面是通过 json 配置文件的方式对 logging 进行配置。

{"version": 1,"disable_existing_loggers": false,"formatters": {"simple": {"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"}},"handlers": {"console": {"class": "logging.StreamHandler","level": "DEBUG","formatter": "simple","stream": "ext://sys.stdout"},"info_file_handler": {"class": "logging.handlers.RotatingFileHandler","level": "INFO","formatter": "simple","filename": "info.log","encoding": "utf8"}},"loggers": {"my_module": {"level": "ERROR","handlers": ["info_file_handler"],"propagate": "no"}},"root": {"level": "INFO","handlers": ["console","info_file_handler"]}
}

在 python 中读取配置文件:

{"version": 1,"disable_existing_loggers": false,"formatters": {"simple": {"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"}},"handlers": {"console": {"class": "logging.StreamHandler","level": "DEBUG","formatter": "simple","stream": "ext://sys.stdout"},"info_file_handler": {"class": "logging.handlers.RotatingFileHandler","level": "INFO","formatter": "simple","filename": "info.log","encoding": "utf8"}},"loggers": {"my_module": {"level": "ERROR","handlers": ["info_file_handler"],"propagate": "no"}},"root": {"level": "INFO","handlers": ["console","info_file_handler"]}
}

参考文档

[1] python基础学习十 logging模块详细使用 https://www.cnblogs.com/louis-w/p/8567434.html
[2] github django logging https://github.com/django/django/blob/1586a09b7949bbb7b0d84cb74ce1cadc25cbb355/django/utils/log.py#L18

相关文章:

Python logging 模块详解

Python 的 logging 模块提供了一个强大而灵活的日志系统。它是 Python 标准库的一部分&#xff0c;因此可以在任何 Python 程序中使用。logging 模块提供了许多有用的功能&#xff0c;包括日志消息的级别设置、日志消息的格式设置、将日志消息输出到不同的目标&#xff0c;以及…...

http://account.battlenet.com.cn

http://account.battlenet.com.cn 魔兽战网 短信验证 查了下&#xff0c;我老早以前账号还在&#xff0c;纪念下&#xff0c;少玩游戏。...

java第二十课 —— 面向对象习题

类与对象练习题 编写类 A01&#xff0c;定义方法 max&#xff0c;实现求某个 double 数组的最大值&#xff0c;并返回。 public class Chapter7{public static void main(String[] args){A01 m new A01();double[] doubleArray null;Double res m.max(doubleArray);if(res !…...

Flask的模块化实践

既作为前端&#xff0c;又作为后端的我&#xff0c;写flask写了那么多行了&#xff0c;其实它们属于不同的模块&#xff0c;比如登录&#xff0c;注册&#xff0c;聊天&#xff0c;用户画像&#xff0c;那我觉得有必要分一下了&#xff0c;系统化的处理一下&#xff0c;不然找个…...

锁存器(Latch)的产生与特点

Latch 是什么 Latch 其实就是锁存器&#xff0c;是一种在异步电路系统中&#xff0c;对输入信号电平敏感的单元&#xff0c;用来存储信息。锁存器在数据未锁存时&#xff0c;输出端的信号随输入信号变化&#xff0c;就像信号通过一个缓冲器&#xff0c;一旦锁存信号有效&#…...

搜维尔科技:「案例」Faceware电影中面部动画的演变历程

面部动画是电影中角色表演的一个重要方面&#xff0c;尤其是在严重依赖电子动画、化妆效果和动作捕捉系统的奇幻电影中。在《龙与地下城&#xff1a;盗贼荣誉》电影中&#xff0c;龙裔角色的面部动画是一个复杂的系统&#xff0c;使该生物在大屏幕上栩栩如生。该系统依赖于一种…...

特征工程技巧—Bert

前段时间在参加比赛&#xff0c;发现有一些比赛上公开的代码&#xff0c;其中的数据预处理步骤值得我们参考。 平常我们见到的都是数据预处理&#xff0c;现在我们来讲一下特征工程跟数据预处理的区别。 数据预处理是指对原始数据进行清洗、转换、缩放等操作&#xff0c;以便为…...

更改 Docker 的默认存储位置

记录一下使用 Docker 遇到的问题&#xff0c;Docker 也用得比较多&#xff0c;最近发现根目录所在磁盘快满了&#xff0c;发现是 Docker 默认会将镜像和容器等数据保存在目录 /var/lib/docker 目录下&#xff0c;我们可以更改 Docker 的默认存储位置&#xff0c;比如改到数据盘…...

搜索与图论:图中点的层次

搜索与图论&#xff1a;图中点的层次 题目描述参考代码 题目描述 输入样例 4 5 1 2 2 3 3 4 1 3 1 4输出样例 1参考代码 #include <cstring> #include <iostream> #include <algorithm>using namespace std;const int N 100010;int n, m; int h[N], e[N]…...

NLP入门——数据预处理:编码规范化

编码规范化 在计算机中&#xff0c;我们需要将字符与字节序列之间建立起映射关系&#xff0c;这个过程被称为编码。有许多不同的编码方式&#xff0c;例如 ASCII、UTF-8、UTF-16 和 GBK 等。这些编码方式会将每个字符编码为一个或多个字节&#xff0c;以便于在计算机、网络和其…...

代码随想录算法训练营第四十八天| 70. 爬楼梯 (进阶)、322. 零钱兑换、279.完全平方数

70. 爬楼梯 &#xff08;进阶&#xff09; 题目链接&#xff1a;70. 爬楼梯 &#xff08;进阶&#xff09; 文档讲解&#xff1a;代码随想录/爬楼梯 &#xff08;进阶&#xff09; 状态&#xff1a;已完成&#xff08;0遍&#xff09; 解题过程 这几天博主忙着面试和入职&am…...

c++11 constexpr关键字

constexpr 是 C11 引入的一个关键字&#xff0c;它允许在编译时计算表达式的值&#xff0c;并将这些值存储在程序的常量部分中。这意味着 constexpr 变量和函数可以在编译时进行求值&#xff0c;从而避免了运行时的开销。 constexpr变量 constexpr 变量必须在编译时初始化&am…...

ios 获取图片的一部分区域

可以使用如下的代码&#xff1a; // get part of the image - (UIImage *)getPartOfImage:(UIImage *)img rect:(CGRect)partRect {CGImageRef imageRef img.CGImage;CGImageRef imagePartRef CGImageCreateWithImageInRect(imageRef, partRect);UIImage *retImg [UIImage i…...

数据结构(3)栈、队列、数组

1 栈 1.1 栈的定义 后进先出【LIFO】 1.2 基本操作 元素进栈出栈 只能在栈顶进行&#xff01;&#xff01;&#xff01; 经常考的题&#xff1a; 穿插的进行进栈和出栈 可能有多个选项 1.3 顺序栈 1.3.1 初始化 下标是从0开始的 1.3.2 进栈 更简单的写法&#xff1a; 1.3…...

数据库(入门)

文章目录 一、数据库(DB) 二、数据库管理系统&#xff08;DBMS&#xff09; 三、SQL&#xff08;结构化查询语言&#xff09; 四、三者的关系 五、端口号&#xff08;port number&#xff09; 一、数据库(DB) 定义&#xff1a;按照一定格式存储数据的一些文件的组合。 简单来…...

ChatTTS+Python编程搞定语音报时小程序

文字转语音神器Python编程搞定语音报时小程序 今天一个好哥们发了一个文字转语音的AI神器的短视频。这个神器的网站是[ChatTTS - Text-to-Speech for Conversational Scenarios][https://chattts.com/]&#xff0c;如下图所示&#xff1a; 这个开源项目可以从github.com上下载…...

【Mac】Alfred 5 for Mac(苹果效率提升工具)v5.5软件介绍及安装教程

软件介绍 Alfred 是适用于 Mac 操作系统的流行生产力应用程序。它旨在帮助用户在 Mac 电脑上更高效地启动应用程序、搜索文件和文件夹以及执行各种任务。借助 Alfred&#xff0c;用户可以创建自定义键盘快捷方式、设置自定义工作流程并使用热键访问功能。 Alfred for Mac 的一…...

PDF文件处理不再复杂:9个Python库让一切变得简单

大家好&#xff0c;这里是程序员晚枫&#xff0c;2年前发布了一个开源项目&#xff1a;python-office&#xff0c;目前在GitHub上有800⭐&#xff0c;最近在开发新功能时感觉Python知识有点不够用了。 所以打算从2方面补充自己的知识&#xff1a;研究优秀的第三方库和学习Pyth…...

安防视频融合汇聚平台EasyCVR如何实现视频画面自定义标签?

安防视频融合汇聚平台EasyCVR兼容性强&#xff0c;可支持Windows系统、Linux系统以及国产化操作系统等&#xff0c;平台既具备传统安防视频监控的能力&#xff0c;也具备接入AI智能分析的能力&#xff0c;可拓展性强、视频能力灵活&#xff0c;能对外分发RTMP、RTSP、HTTP-FLV、…...

Liunx音频

一. echo -e "\a" echo 通过向控制台喇叭设备发送字符来发声&#xff1a; echo -e "\a"&#xff08;这里的 -e 选项允许解释反斜杠转义的字符&#xff0c;而 \a 是一个响铃(bell)字符&#xff09; 二. beep 下载对应的包 yum -y install beep 发声命令 be…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...

libfmt: 现代C++的格式化工具库介绍与酷炫功能

libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库&#xff0c;提供了高效、安全的文本格式化功能&#xff0c;是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全&#xff1a…...

Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合

作者&#xff1a;来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布&#xff0c;Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明&#xff0c;Elastic 作为 …...

Vue 实例的数据对象详解

Vue 实例的数据对象详解 在 Vue 中,数据对象是响应式系统的核心,也是组件状态的载体。理解数据对象的原理和使用方式是成为 Vue 专家的关键一步。我将从多个维度深入剖析 Vue 实例的数据对象。 一、数据对象的定义方式 1. Options API 中的定义 在 Options API 中,使用 …...

GB/T 43887-2024 核级柔性石墨板材检测

核级柔性石墨板材是指以可膨胀石墨为原料、未经改性和增强、用于核工业的核级柔性石墨板材。 GB/T 43887-2024核级柔性石墨板材检测检测指标&#xff1a; 测试项目 测试标准 外观 GB/T 43887 尺寸偏差 GB/T 43887 化学成分 GB/T 43887 密度偏差 GB/T 43887 拉伸强度…...

构建Docker镜像的Dockerfile文件详解

文章目录 前言Dockerfile 案例docker build1. 基本构建2. 指定 Dockerfile 路径3. 设置构建时变量4. 不使用缓存5. 删除中间容器6. 拉取最新基础镜像7. 静默输出完整示例 docker runDockerFile 入门syntax指定构造器FROM基础镜像RUN命令注释COPY复制ENV设置环境变量EXPOSE暴露端…...

开疆智能Ethernet/IP转Modbus网关连接鸣志步进电机驱动器配置案例

在工业自动化控制系统中&#xff0c;常常会遇到不同品牌和通信协议的设备需要协同工作的情况。本案例中&#xff0c;客户现场采用了 罗克韦尔PLC&#xff0c;但需要控制的变频器仅支持 ModbusRTU 协议。为了实现PLC 对变频器的有效控制与监控&#xff0c;引入了开疆智能Etherne…...