logging模块
1 从结绳记事到日志
在远古时代,人类使用结绳记事记录重要事件——绳结的大小、位置和颜色代表不同含义。随着文明发展,我们有了纸笔、印刷术,直到今天的电子日志系统。
Python的logging模块就是数字时代的"结绳记事",帮助开发者记录程序运行的关键信息。
2 为什么需要日志
日志和保险一样,系统没有问题时没有存在感,出了问题后承担“行车记录仪”的功能。
日志记录应用程序和基础设施运行中的每一个关键事件、操作和消息,包括所发生事件的描述、相关时间戳、与事件相关的严重性级别类型以及其他相关元数据。日志对于调试问题、诊断错误和审核系统活动非常有用。它们提供了系统事件的文本叙述,使人们更容易理解所采取的操作顺序。
3 Python日志模块概述

Python 的 logging 模块通过层级化的日志器(Logger)、处理器(Handler)、过滤器(Filter) 和 格式化器(Formatter) 实现灵活的日志管理。
Logger
日志级别:
DEBUG<INFO<WARNING<ERROR<CRITICAL,日志仅当级别≥日志器级别时才会处理。Filter
精细化控制:基于日志内容(如级别、消息文本、模块名)决定是否处理日志。
绑定位置:可附加到日志器(全局过滤)或处理器(目标特定过滤)。
Handler
负责将日志传递到不同目标:
控制台处理器:
logging.StreamHandler(输出到sys.stdout/stderr)。文件处理器:
logging.FileHandler(写入文件)、RotatingFileHandler(滚动文件)。其他处理器:如
SMTPHandler(邮件)、SocketHandler(网络)等。多处理器支持:一个日志器可绑定多个处理器(如同时输出到控制台和文件)。
Formatter
将日志记录(
LogRecord)转换为字符串(如添加时间、模块名)

4 日志使用常见错误
4.1 没有log或者少打log
解决方案:用装饰器简化代码。
例如我们有三个函数, add 、 mul 、 div
如果我们需要记录函数的入口参数、返回结果和异常信息,以往需要在每个函数中重复编写日志代码,显得冗余且费力。
以下是优化后的实现:
通过装饰器封装日志记录代码,消除每个函数中的重复日志语句
自动记录函数名、参数、返回值和异常信息
输出示例:
4.2 log信息不全
缺少上下文信息
解决方案:使用LogAdapter简化操作
有时打印log时容易信息不全,容易混淆两个类的信息,例如:
log后的信息都是
Eating apple,同时,如果类的属性有删减,也需要一个一个修改logging的代码,非常麻烦。这时可以使用log的extra来简化操作,代码如下:
extra的内容也可以在logging模块中全局配置:
输出示例:
报错缺少堆栈信息
解决方案:设置
exc_info=True,代码示例如下:需要临时更换context
解决方案:实现LoggingContext类,临时改变log的行为(monkey patch)
4.3 level不合适导致磁盘占满
解决方案:可以使用 RotatingHandler 或者 TimeRotatingHandler
4.4 logging浪费过多CPU资源
解决方案:注意log的风格
Python logging 的官方文档明确建议使用占位符格式:
"将变量作为参数传递给日志记录方法,而非自行格式化字符串。"
当使用
%或{}占位符 + 参数传递时,仅在日志实际需要输出时(如当前日志级别≥设置的级别)才会进行字符串格式化。而
.format()或 f-string 会立即执行格式化,即使日志被过滤(例如当前级别为ERROR,但调用的是INFO日志)。节省资源:在高并发或频繁记录低级别日志(如
DEBUG)时,避免不必要的字符串操作。
下面是代码示例:
5 总结
从远古的绳结到现代的日志系统,记录的核心目的从未改变——保存重要信息,传递关键知识。Python的logging模块提供了强大而灵活的日志解决方案,是每个Python开发者必须掌握的核心技能之一。
参考
Last updated