Qt6开发跨平台音乐播放器(七):日志处理

1. 概述

在 Qt 应用程序开发中,有效的日志处理是快速排查故障的关键部分,特别是在终端机器上,收集日志进行分析几乎是唯一的手段,所以这一节我们结合Qt的日志模块来设计一个简单的日志类,实现以下几个目标:

  • 日志级别控制

  • 自定义日志内容

  • 日志输出到文件

2. 实现

2.1 日志级别控制

Qt的日志模块本身已经具备这一能力了,qInfo、qDebug、qWarning、qCritical和qFatal代表了5个不同的日志级别:

函数级别备注
qInfo提示信息
qDebug调试信息
qWarning警告信息
qCritical严重错误
qFatal致命错误调用后会导致程序退出
// PS:这5个函数都有C风格和C++风格两种调用方法:
qDebug("this is a %c style debug log", 'c');
qDebug() << "this is a " << "cpp style " << "log";

然后使用QLoggingCategory来控制日志的输出,这里有四种方法:

第一种是获取程序默认的category对象,然后调用setEnable函数来设置:

// 禁用debug信息
// 五种级别对应QtDebugMsg、QtInfoMsg、QtWarningMsg、QtCriticalMsg和QtFatalMsg
QLoggingCategory::defaultCategory()->setEnabled(QtDebugMsg, false);

第二种是调用静态的setFilteRules函数:

// 禁用debug信息
// 五种级别对应debug、info、warning、critical和fatal
// 可以同时设置多个级别,用\n分割
QLoggingCategory::setFilterRules("*.debug=false");

第三种是定义QT_LOGGING_CONF环境变量,值指向一个配置文件,而内容是过滤规则,比如:

[Rules]
*=true
*.debug=false

第四种是定义QT_LOGGING_RULES环境变量,值是过滤规则,比如:

*=false;*.debug=true

注意:环境变量的设置会覆盖setFilterRules设置,而QLoggingCategory这个类也有更强大的功能,比如自定义Category,感兴趣的可以参考Qt文档。

2.2 自定义日志内容

可以使用Qt的qSetMessagePattern函数来自定义日志内容,其定义如下:

void qSetMessagePattern(const QString &pattern)

而pattern是一串由多个占位符组成的字符串,比如:

QString example="[%{time yyyy-MM-dd hh:mm:ss} %{type}] %{message}";
qSetMessagePattern(example);

// 输出的日志内容类似:
// [2024-09-29 02:06:23 debug] test logger

所有支持的占位符如下:

占位符描述
%{appname}程序名称
%{category}日志类别
%{file}文件路径
%{function}函数
%{line}源码行
%{message}消息正文
%{pid}进程号
%{threadid}线程号
%{type}“debug”, “warning”, “critical”, “fatal”
%{time process}自进程启动到消息打印时的秒数
%{time boot}自系统启动到消息打印时的秒数
%{time [format]}自定义格式的时间(同QDatetime的toString函数)

而另一种方法是定义QT_MESSAGE_PATTERN环境变量,内容与qSetMessagePattern是一样的,但环境变量的优先级更高。

2.3 日志输出到文件

Qt提供了qInstallMessageHandler来安装一个日志拦截器,在拦截器里操作日志内容。由于拦截器会覆盖上面提到日志级别控制和日志内容格式,所以我们只在release模式下使用,而debug模式下仍旧使用QLoggingCategory::setFilterRules和qSetMessagePattern来控制。

创建一个logger模块,包含Logger类:

logger
 - logger.pri
 - logger.h
 - logger.cpp

定义一个静态函数,在该函数中完成所有跟日志相关的设置,这样调用就很方便了:

// logger.h
class Logger {
public:
    static void enable();
}

// logger.cpp
void Logger::enable() {
    qInstallMessageHandler(logHandler);
}

在logHandler函数中进行日志级别控制、日志内容格式定义和输出到文件等操作:

void logHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
    static QMutex mutex;
    QMutexLocker locker(&mutex);

    // only output critical log under release mode
    if (QtCriticalMsg != type) return;

    // create log directory
    QString dirPath = QCoreApplication::applicationDirPath() + "/logs/";
    QDir dir(dirPath);
    if ((!dir.exists(dirPath)) && (!dir.mkpath(dirPath))) {
        return;
    }

    QString finalMsg = QString("[%1 %2 %3 %4] %5")
                           .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz"))
                           .arg(type)
                           .arg(context.file)
                           .arg(context.line)
                           .arg(msg);

    // output file
    QFile file(dirPath + QDateTime::currentDateTime().toString("yyMMddhh") + ".log");
    file.open(QIODevice::WriteOnly | QIODevice::Append);
    QTextStream stream(&file);
    stream << finalMsg << "\r\n";
    file.flush();
    file.close();
}

由于release模式默认不显示日志中的文件、行数和函数信息,需要在pro文件或pri文件中添加一个配置:

CONFIG(release, debug|release) {
    DEFINES += QT_MESSAGELOGCONTEXT
}

这样,一个小而美的日志处理模块就完成了。


PS: 代码已经开源在github:linqiaqun/music-player: A cross platform music player (github.com) 欢迎star/fork/issue

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值