Python3 logging 模块及应用

🎉摘要:本文详解 Python 内置 logging 日志模块,涵盖日志级别、核心组件配置、文件切割策略及异常堆栈捕获。提供生产环境可用的日志记录封装示例,帮助开发者高效记录运行信息与排查问题。

做开发,对日志应该很清楚也很敏感吧。每当我们项目出现问题时,解决问题第一步是看日志,分析日志,可以想象日志的重要性。

在 Python 中,已经内置了日志模块 logging,当我们使用 python 进行项目开发时,可以使用该模块记录日志。和其他语言(如 java 的 logback)的语言日志库类似,logging 模块也支持很多配置,如配置日志级别、配置日志输出格式、存放位置、日志滚动存储策略等等。

日志级别

日志级别控制日志输出粒度,日志级别越低输出的内容越多,越高输出的内容越多。例如,开发环境我们设置日志级别为 DEBUG,生产环境设置日志级别为 WARNING,两者输出的日志多少不言而喻。开发环境要求日志越详细越好,方便定位问题。而生产环境的代码都是经过反复测试,没有必要大很多详细日志,将警告以上的日志输出就够了。

注意,logging 模块只输出 >= 当前设置级别的日志。

级别数值说明
DEBUG10调试细节(开发用)
INFO20程序正常运行信息
WARNING30警告(不影响运行)
ERROR40错误(部分功能失败)
CRITICAL50严重错误(程序崩溃)

默认情况下,logging 模块的日志级别为 WARNING,低于它的 DEBUG/INFO 不会输出。

核心组件

logging 模块有四个核心组件,分别如下:

Logger

日志记录器,是日志入口,开发者直接调用的对象,用来发起 debug/info/warning/error/critical 各级日志。例如:

logging.debug("调试信息:连接数据库成功")
logging.info("普通信息:程序开始运行")

Handler

日志最终输出的目的地处理器,负责把日志消息分发、输出到不同位置。例如控制台、文件、网络、数据库等等。

常用内置 Handler 如下:

  • StreamHandler:输出到控制台 / 标准流

  • FileHandler:写入本地文件

  • RotatingFileHandler:按文件大小切割日志

  • TimedRotatingFileHandler:按时间自动切割日志

Formatter

日志格式控制组件,用于控制每行日志输出的格式,如时间、级别、文件名等等。

常用参数:

  • %(asctime)s:时间

  • %(levelname)s:日志级别

  • %(filename)s:文件名

  • %(lineno)d:行号

  • %(message)s:日志信息

Filter

日志过滤器,用来准确筛选或者过滤日志,决定一条日志是否允许被输出。

核心逻辑为自定义 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 日志文件,如下图:

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

自定义 Logger

和其他语言的日志组件类似,该日志模块也支持同时输出到控制台和日志文件。例如:

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 文档。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号