PySide6从0开始学习的笔记(二十七) 日志管理

一、 logging 简介

logging是 Python 内置的日志模块,这个模块是 Python 官方提供的进行程序日志记录的核心工具。

1. logging比print先进的地方
  • print()是最常用的程序调试语句,它能把信息输出到控制台,但无法持久化(比如保存到文件),也无法区分信息的重要程度;
  • logging 可以灵活控制日志的级别 (DEBUG/INFO/WARNING/ERROR/CRITICAL)、输出位置 (控制台、文件、数据库等)、格式(包含时间、模块、行号等),是生产环境中记录程序运行状态的标准方案。
2. 基础使用示例
  • 基础配置方法示例:

    import logging

    基础配置(可选,不配置则使用默认格式输出到控制台)

    logging.basicConfig(
    level=logging.INFO, # 只记录 INFO 及以上级别的日志
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', # 日志格式
    handlers=[ # 可设多个日志处理器
    logging.FileHandler("app_run.log", encoding="utf-8"), # 可选:输出到文件
    logging.StreamHandler() # 可选:输出到控制台
    ]
    )

    输出不同级别的日志

    logging.debug('调试信息:比如程序运行的详细过程,开发时用') # 不会输出(因为级别设为INFO)
    logging.info('普通信息:比如程序正常启动、完成某个操作') # 以下都会输出, 并在日志文件中记录
    logging.warning('警告信息:比如非致命错误,如参数不规范')
    logging.error('错误信息:比如功能执行失败,但程序还能运行')
    logging.critical('严重错误:比如程序无法继续运行')

自定义logger的方法:

python 复制代码
import logging, sys

# 1. 创建/取 logger
logger = logging.getLogger('my.module')   # 名字随意,推荐用模块名
logger.setLevel(logging.DEBUG)            # logger 自身级别

# 2. 创建 handler(可以多个)
h1 = logging.StreamHandler(sys.stdout)    # 控制台
h2 = logging.FileHandler('run.log', encoding='utf-8')  # 文件

# 3. 创建 formatter
fmt = logging.Formatter(
    '%(asctime)s | %(levelname)-8s | %(name)s:%(funcName)s:%(lineno)d | %(message)s'
)
h1.setFormatter(fmt)
h2.setFormatter(fmt)

# 4. 把 handler 挂到 logger
logger.addHandler(h1)
logger.addHandler(h2)

# 5. 使用
logger.debug('debug 信息')
logger.info('普通信息')
logger.warning('警告')
logger.error('错误')
3. 核心特点
  • 内置模块:无需额外安装,Python 自带,直接导入即可使用;
  • 分级控制:可以通过级别过滤日志,比如生产环境只记录 ERROR 及以上,开发环境记录 DEBUG;
  • 灵活输出:支持将日志写入文件、按大小 / 时间分割日志文件、输出到网络等。
总结
  1. logging是Python内置的日志模块,可用于替代 print() 做专业的日志记录;
  2. 该模块支持日志分级、自定义格式、多输出位置,是生产环境程序必备的调试 / 监控工具;

二、Qt 中的日志管理

1、pyside6的日志消息输出函数

Qt中也内置了几个日志消息输出的函数:

  • qDebug():调试信息 开发阶段的诊断信息,Release版可能不显示。
  • qWarning(): 警告信息 发生了小错误或异常,但程序能恢复运行。
  • **qCritical():**严重错误 发生了错误,程序可能部分功能失效。
  • qFatal(): 致命错误 (注意) 会立即终止程序,通常用于检测到无法恢复的逻辑错误。

可以手动调用或由Qt系统在后台自动调用这几个函数,输出相应的Qt日志消息。

Qt日志消息的类型为以下几种:

  • **QtMsgType.QtDebugMsg:**调试信息
  • **QtMsgType.QtInfoMsg:**状态信息
  • **QtMsgType.QtWarningMsg:**警告信息
  • **QtMsgType.QtCriticalMsg:**严重错误
  • **QtMsgType.QtFatalMsg:**致命错误

每调用一次消息输出函数会生成一条对应类型的日志消息,比如:运行qDebug()会生成一条QtDebugMsg类型的消息。但需注意:pyside6并未提供暴露给开发者的qInfo() 函数来生成QtInfoMsg类型的消息。

2、安装消息管理器来管理日志信息

Qt的日志消息需要使用PySide6.QtCore.qInstallMessageHandler来安装一个管理器来管理。qInstallMessageHandler的基本用法:

python 复制代码
qInstallMessageHandler(qt_message_handler)

qt_message_handler即为指定的消息处理函数,日志消息输出函数每输出一条消息就调用一次消息处理函数。

  • 调用示例:
python 复制代码
import sys

from PySide6.QtCore import qDebug, qInstallMessageHandler, QMessageLogContext
from PySide6.QtWidgets import QApplication


def qt_message_handler(msg_type, context: QMessageLogContext, msg):
    print(f"消息类型:{msg_type}")
    print(f"消息上下文的类别:{context.category}")
    print(f"消息内容:{msg}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    qInstallMessageHandler(qt_message_handler)     # Qt日志消息由qt_message_handlerr来处理
    qDebug("这是调试信息,仅开发阶段使用")  # 调用qDebug会生成一条QtMsgType.QtDebugMsg类型的消息并触发qt_message_handler函数来处理
    app.quit()
    sys.exit()
  • 代码解释:

1、qInstallMessageHandler(qt_message_handler) : Qt日志消息由qt_message_handlerr来函数处理。

2、qDebug("这是调试信息,仅开发阶段使用"):调用qDebug生成一条QtMsgType.QtDebugMsg类型的日志消息,并触发qt_message_handler(日志消息处理函数)来处理调用。

日志消息处理函数接受3个参数:

**msg_type:**消息类型,比如QtMsgType.QtDebugMsg、QtMsgType.QtWarningMsg等。

context: 消息上下文(格式为QMessageLogContext),消息上下文的属性有:

category: 类别

file: 运行的文件

function: 生生消息的函数

line: 生成消息的代码行数

**version :**版本

**msg:**消息内容。

  • 一个获取Qt系统运行消息的范例:
python 复制代码
import sys
import time

from PySide6.QtCore import qInstallMessageHandler, QThread
from PySide6.QtWidgets import QApplication, QPushButton


def qt_message_handler(msg_type, context, msg):
    print(f"消息类型:[{msg_type}], 消息内容:[{msg}]")

def on_btn_clicked():
    app.quit()  # 退出应用

class thread(QThread):
    def __init__(self):
        super().__init__()

    def run(self):
        while True:
            print("线程正在运行")
            time.sleep(1)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    qInstallMessageHandler(qt_message_handler)     # Qt日志消息由qt_message_handlerr来处理
    btn = QPushButton("按钮")
    btn.clicked.connect(on_btn_clicked)
    btn.show()
    thread = thread()  # 创建一个持续运行的线程对象
    thread.start()
    sys.exit(app.exec())

运行结果:

python 复制代码
线程正在运行
线程正在运行
线程正在运行
线程正在运行
消息类型:[QtMsgType.QtFatalMsg], 消息内容:[QThread: Destroyed while thread is still running]

进程已结束,退出代码为 -1073740791 (0xC0000409)

上面的代码中,点击按钮连接了应用的退出函数(app.quit),应用的退出销毁了一个正在运行的线程,触发了一条QtMsgType.QtFatalMsg类型的日志消息。

在使用Pycharm调试程序时,经常会遇到这种"进程已结束,退出代码为 -1073740791 (0xC0000409)",莫名其妙就退出了,也没有提示退出原因,使用上面代码的方式就可以很方便地通过读取Qt日志消息来知道发生了什么。

  • 修改后比较完善的代码:
python 复制代码
import logging
import sys

from PySide6.QtCore import qInstallMessageHandler, QThread, QTimer
from PySide6.QtWidgets import QApplication, QPushButton


def qt_message_handler(msg_type, context, msg):
    print(f"消息类型:[{msg_type}], 消息内容:[{msg}]")

def on_btn_clicked():
    app.quit()  # 退出应用

# 退出应用之前的处理函数
def about_quit():
    work_thread.stop_flag = True
    work_thread.quit()
    work_thread.wait(3000)
    work_thread.deleteLater()


class WorkThread(QThread):
    def __init__(self):
        super().__init__()
        self.stop_flag = False

    def run(self):
        while not self.stop_flag:
            logger.debug("线程正在运行")
            QTimer.singleShot(0, lambda :None)  # 空定时事件,防止线程陷入死循环,保持接受其他Qt事件的能力
            QThread.msleep(1000)
        logger.debug("线程已停止")


if __name__ == "__main__":
    app = QApplication(sys.argv)

    # 创建一个日志记录器
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)  # 设置日志级别为 DEBUG
    logger.addHandler(logging.StreamHandler(sys.stdout))  # 将日志输出到控制台

    # Qt日志消息由qt_message_handler来处理
    qInstallMessageHandler(qt_message_handler)

    btn = QPushButton("按钮")
    btn.clicked.connect(on_btn_clicked)
    btn.show()

    # 创建一个持续运行的线程对象
    work_thread = WorkThread()
    work_thread.start()

    # 连接应用退出之前的处理函数
    app.aboutToQuit.connect(about_quit)

    sys.exit(app.exec())

三、结合上面二者,构建日志管理系统

Qt的日志消息默认输出到控制台和命令行,不支持直接将日志写入文件等其他输出途径。logging是Python内置的日志模块,它无需额外安装,支持分级控制,支持将日志写入文件、按大小 / 时间分割日志文件、输出到网络等,但它不直接支持读取Qt格式的日志消息。可以使用qInstallMessageHandler将Qt的日志消息交由logging管理,将二者整合。

python 复制代码
import sys
import logging
from time import asctime

from PySide6.QtCore import qInstallMessageHandler, QtMsgType, qWarning
from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton, QVBoxLayout


# 配置 Python logging 基础设置
logging.basicConfig(
    level=logging.DEBUG,  # 设置日志级别为 DEBUG
    format="%(asctime)s - %(levelname)s - %(message)s",  # 设置日志格式
    handlers=[  # 设置日志处理器
        logging.FileHandler("app_run.log", encoding="utf-8"),  # 可选:输出到文件
        logging.StreamHandler(sys.stdout)  # 可选:输出到控制台
    ]
)
logger = logging.getLogger(__name__)   # 获取当前模块的日志记录器


# 将 Qt 内部消息转发到 Python logging
def qt_to_python_log_handler(msg_type, context, msg):
    """把 Qt 消息转换成 Python logging 日志"""
    if msg_type == QtMsgType.QtDebugMsg:
        logger.debug(f"Qt: {msg}")
    elif msg_type == QtMsgType.QtInfoMsg:
        logger.info(f"Qt: {msg}")
    elif msg_type == QtMsgType.QtWarningMsg:
        logger.warning(f"Qt: {msg}")
    elif msg_type == QtMsgType.QtCriticalMsg:
        logger.error(f"Qt: {msg}")
    elif msg_type == QtMsgType.QtFatalMsg:
        logger.critical(f"Qt: {msg}")


class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("日志整合示例")
        # 业务日志(用 Python logging)
        logger.info("程序启动,主窗口初始化")
        btn1 = QPushButton("触发 Qt 警告", self)
        # 触发 Qt 警告
        btn1.clicked.connect(self.qt_warnning_slot)
        btn2 = QPushButton("触发 Python 警告", self)
        btn2.clicked.connect(self.python_warnning_slot)
        layout = QVBoxLayout()
        layout.addWidget(btn1)
        layout.addWidget(btn2)
        self.setLayout(layout)

    def qt_warnning_slot(self):
        qWarning("触发 Qt 警告")

    def python_warnning_slot(self):
        logger.warning("触发 Python 警告")


if __name__ == "__main__":
    # 安装 Qt 消息处理器,转发到 Python logging
    qInstallMessageHandler(qt_to_python_log_handler)     # 安装之后,Qt内部的日志由qt_to_python_log_handler来处理

    app = QApplication(sys.argv)
    window = MainWindow()

    window.show()
    sys.exit(app.exec_())
相关推荐
天天进步20152 小时前
Python全栈项目:实时数据处理平台
开发语言·python
Tipriest_2 小时前
Python中is关键字详细说明,比较的是地址还是值
开发语言·python
sheji34162 小时前
【开题答辩全过程】以 基于Python的餐饮统计系统的设计和实 现为例,包含答辩的问题和答案
开发语言·python
xqqxqxxq2 小时前
Java Thread 类核心技术笔记
java·笔记
慎独4132 小时前
科学赋能,让孩子专注高效爱上学习
学习
LGL6030A2 小时前
Java学习历程26——线程安全
java·开发语言·学习
遨游xyz2 小时前
排序-快速排序
开发语言·python·排序算法
老师用之于民2 小时前
【DAY21】Linux软件编程基础&Shell 命令、脚本及系统管理实操
linux·运维·chrome·经验分享·笔记·ubuntu
iFeng的小屋2 小时前
【2026年新版】Python根据小红书关键词爬取所有笔记数据
笔记·爬虫·python