做开发,对日志应该很清楚也很敏感吧。每当我们项目出现问题时,解决问题第一步是看日志,分析日志,可以想象日志的重要性。
在 Python 中,已经内置了日志模块 logging,当我们使用 python 进行项目开发时,可以使用该模块记录日志。和其他语言(如 java 的 logback)的语言日志库类似,logging 模块也支持很多配置,如配置日志级别、配置日志输出格式、存放位置、日志滚动存储策略等等。
日志级别控制日志输出粒度,日志级别越低输出的内容越多,越高输出的内容越多。例如,开发环境我们设置日志级别为 DEBUG,生产环境设置日志级别为 WARNING,两者输出的日志多少不言而喻。开发环境要求日志越详细越好,方便定位问题。而生产环境的代码都是经过反复测试,没有必要大很多详细日志,将警告以上的日志输出就够了。
注意,logging 模块只输出 >= 当前设置级别的日志。
| 级别 | 数值 | 说明 |
| DEBUG | 10 | 调试细节(开发用) |
| INFO | 20 | 程序正常运行信息 |
| WARNING | 30 | 警告(不影响运行) |
| ERROR | 40 | 错误(部分功能失败) |
| CRITICAL | 50 | 严重错误(程序崩溃) |
默认情况下,logging 模块的日志级别为 WARNING,低于它的 DEBUG/INFO 不会输出。
logging 模块有四个核心组件,分别如下:
日志记录器,是日志入口,开发者直接调用的对象,用来发起 debug/info/warning/error/critical 各级日志。例如:
logging.debug("调试信息:连接数据库成功")
logging.info("普通信息:程序开始运行")日志最终输出的目的地处理器,负责把日志消息分发、输出到不同位置。例如控制台、文件、网络、数据库等等。
常用内置 Handler 如下:
StreamHandler:输出到控制台 / 标准流
FileHandler:写入本地文件
RotatingFileHandler:按文件大小切割日志
TimedRotatingFileHandler:按时间自动切割日志
日志格式控制组件,用于控制每行日志输出的格式,如时间、级别、文件名等等。
常用参数:
%(asctime)s:时间
%(levelname)s:日志级别
%(filename)s:文件名
%(lineno)d:行号
%(message)s:日志信息
日志过滤器,用来准确筛选或者过滤日志,决定一条日志是否允许被输出。
核心逻辑为自定义 filter(record) 方法,返回 True 输出日志、False 拦截丢弃。
过滤器常见用途:按日志名称、模块、自定义字段、级别外的条件细粒度过滤,比如只过滤某个模块的 debug 日志、屏蔽指定关键字日志等等。
直接调用 logging 内置方法,如下:
import logging
# 基础输出(默认只输出 WARNING 及以上)
logging.debug("调试信息:连接数据库成功")
logging.info("普通信息:程序开始运行")
logging.warning("警告信息:磁盘空间不足")
logging.error("错误信息:文件读取失败")
logging.critical("严重错误:程序崩溃")运行结果:
WARNING:root:警告信息:磁盘空间不足
ERROR:root:错误信息:文件读取失败
CRITICAL:root:严重错误:程序崩溃通过 logging.basicConfig() 设置日志级别、格式、输出到哪个文件,如下:
import logging
# 配置日志:级别+格式+输出到文件
logging.basicConfig(
level=logging.DEBUG, # 设置最低级别:输出所有日志
format="%(asctime)s - %(levelname)s - %(message)s", # 日志格式
datefmt="%Y-%m-%d %H:%M:%S", # 时间格式
filename="app.log", # 输出到文件
filemode="a", # 追加模式(w=覆盖,a=追加)
encoding="utf-8" # 编码,可用于解决中文乱码问题
)
# 测试日志
logging.debug("调试日志")
logging.info("信息日志")
logging.warning("警告日志")
logging.error("错误日志")运行脚本,当前目录多了一个 app.log 日志文件,如下图:

打开日志文件,内容如下:

和其他语言的日志组件类似,该日志模块也支持同时输出到控制台和日志文件。例如:
import logging
# 创建日志记录器
logger = logging.getLogger("my_app")
logger.setLevel(logging.DEBUG) # 设置最低级别
# 创建控制台处理器
console_handler = logging.StreamHandler() # 控制台
console_handler.setLevel(logging.INFO) # 控制台只输出 INFO+
# 创建文件处理器
file_handler = logging.FileHandler("app.log", encoding="utf-8") # 文件
file_handler.setLevel(logging.DEBUG) # 文件输出所有日志
# 设置日志格式,控制台和文件使用相同的格式
formatter = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s"
)
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)
# 添加处理器到 logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# 测试日志
logger.debug("数据库连接成功")
logger.info("接口请求成功")
logger.warning("参数为空")
logger.error("数据写入失败")运行脚本,控制台输出如下:
2026-04-24 14:33:45,314 - my_app - INFO - demo.py:28 - 接口请求成功
2026-04-24 14:33:45,315 - my_app - WARNING - demo.py:29 - 参数为空
2026-04-24 14:33:45,315 - my_app - ERROR - demo.py:30 - 数据写入失败日志文件内容如下:

程序上线后,通常可以稳定运行很久,这就导致程序输出的日志越来越多,最终磁盘存不下。然而,这些日志大部分都是没有用的,排查问题通常就最近几天的日志就可以了。
为了解决日志越来越多,磁盘存不下的问题,logging 模块支持对日志自动切割归档,切割可以按日志文件大小、或者根据时间进行切割。
顾名思义,按文件大小,即一个日志文件只存固定大小的内容。一旦日志大小超过了就创建一个新的日志文件来存储,对久日志文件进行归档存储。通常还配合允许保留几个日志备份,多的备份文件将被直接删除(注意,删除是删除最旧的日志文件,保留最新的日志文件)。例如:
import logging
# 引入滚动日志处理器
from logging.handlers import RotatingFileHandler
logger = logging.getLogger("size_log")
logger.setLevel(logging.DEBUG)
# 最大 1MB,保留 3 个备份
handler = RotatingFileHandler(
"size.log",
maxBytes=1024*1024*1, # 最大大小为1MB
backupCount=3, # 备份数量
encoding="utf-8"
)
logger.addHandler(handler)
# 测试
for i in range(1000):
logger.info(f"测试日志 {i}")运行脚本,当前目录如下图:

一个日志文件 size.log,该文件保存最新的日志数据,3 个备份日志文件 size.log.1、size.log.2 和 size.log.3。
有了上面的基础,这个也就更容易了,按时间切割就是指按时间长度切割,如按天切割,表示一天一个日志文件,也可以按小时切割,一个小时一个文件。按时间切割时我们用的最多的,主要原因是方便查询和管理日志,当出现错误时,记住错误的时间,直接旧可以准确的找到记录了改时间段的日志。比按大小记录日志更好查找。
例如:每天 0 点切割日志,最多保留 7 天。
import logging
from logging.handlers import TimedRotatingFileHandler
logger = logging.getLogger("time_log")
logger.setLevel(logging.DEBUG)
# 每天 0 点切割,保留 7 天
handler = TimedRotatingFileHandler(
"time.log",
when="midnight", # 切割频率:S(秒), M(分), H(时), D(天), midnight(凌晨)
backupCount=7, # 保留7个,一天一个日志,7个日志备份不就是7天
encoding="utf-8"
)
logger.addHandler(handler)
logger.info("按时间切割日志")运行日志,效果如下:

经常排查问题的你应该对异常堆栈应该非常了解了吧,异常堆栈中包含了出错的文件,位置,什么错误,可以说是错误的第一案发现场。经验丰富的开发者,通常看一眼异常堆栈就知道程序出了什么问题,可见异常堆栈的重要性。
如果记录日志不记录异常堆栈,那么记录日志的作用将大打折扣。在 logging 模块中,可以通过 logging.exception() 方法记录异常堆栈。这将记录完整报错信息,方便调试,例如:
import logging
logging.basicConfig(level=logging.ERROR, format="%(asctime)s - %(message)s")
try:
1 / 0 # 制造异常
except Exception as e:
logging.exception("发生除零错误") # 自动记录堆栈运行结果:
2026-04-24 14:47:27,829 - 发生除零错误
Traceback (most recent call last):
File "d:\$share_dir\workspace\5.demo\python_demo\demo.py", line 6, in <module>
1 / 0 # 制造异常
~~^~~
ZeroDivisionError: division by zero上面输出中包含完整报错行、错误类型,方便调试和解决问题。
上面示例提供的都是一些零散的代码,本示例通过一个简单的函数封装一个可以应用于生产的日志记录。主要功能如下:
支持同时输出到控制台和文件
支持按时间自动切割日志
支持多模块共用
示例代码:
import logging
import os
from logging.handlers import TimedRotatingFileHandler
def get_logger(name="app"):
"""创建生产环境可用的日志器"""
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG) # 全局最低级别
logger.handlers.clear() # 避免重复添加处理器
# 日志格式
fmt = "%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s"
date_fmt = "%Y-%m-%d %H:%M:%S"
formatter = logging.Formatter(fmt, date_fmt)
# 1. 控制台处理器(输出 INFO+)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)
# 2. 文件处理器(按天切割,保留 15 天)
log_path = "./logs"
os.makedirs(log_path, exist_ok=True)
file_handler = TimedRotatingFileHandler(
filename=os.path.join(log_path, "app.log"),
when="midnight",
backupCount=15,
encoding="utf-8"
)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
# 添加处理器
logger.addHandler(console_handler)
logger.addHandler(file_handler)
return logger
# ==============================================
# 测试使用
# ==============================================
if __name__ == '__main__':
logger = get_logger()
logger.debug("初始化配置完成")
logger.info("服务启动成功")
logger.warning("磁盘使用率超过 80%")
logger.error("数据库连接超时")
# 捕获异常
try:
open("not_exist.txt", "r")
except Exception as e:
logger.exception("文件读取失败")运行结果:
2026-04-24 14:49:08 - INFO - demo.py:46 - 服务启动成功
2026-04-24 14:49:08 - WARNING - demo.py:47 - 磁盘使用率超过 80%
2026-04-24 14:49:08 - ERROR - demo.py:48 - 数据库连接超时
2026-04-24 14:49:08 - ERROR - demo.py:54 - 文件读取失败
Traceback (most recent call last):
File "d:\$share_dir\workspace\5.demo\python_demo\demo.py", line 52, in <module>
open("not_exist.txt", "r")
~~~~^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'not_exist.txt'更多关于 logging 模块的知识,请参考 https://docs.python.org/3/library/logging.html 文档。