Qt日志输出及QsLog日志库

日志输出

Qt提供了多种方式进行日志输出,常用的有qDebug(), qInfo(), qWarning(), qCritical()qFatal()

这些输出函数定义在#include <QDebug>中,并且可以像标准C++的输出流一样使用。

分类说明
qDebug()用于调试信息输出,通常用于开发阶段输出调试信息。
qInfo()用于一般的信息输出,适合输出一般运行状态信息。
qWarning()用于警告信息输出,通常用于提示可能的问题。
qCritical()用于严重错误信息输出,通常用于提示不可恢复的错误。
qFatal()用于致命错误信息输出,通常会中止程序的运行。

C风格输出

qDebug("我是%s,今年%d岁了~","qDebug",20);
qInfo("老铁%d",666);
qWarning("hello %s","warning");
qCritical("helo %s","critical");
qFatal("hello %s","qFatal");		// 注意:qFatal 会中断程序执行

C++风格

qDebug()<<"qDebug";
qInfo()<<"qInfo";
qWarning()<<"qWarnning";
qCritical()<<"qCritical";
#qFatal()<<"qFatal";			   // qFatal()不能用"<<"输出

格式化日志

默认情况下,日志格式是只输出对应的日志内容没有额外信息的。可以通过修改环境变量QT_MESSAGE_PATTERN或者调用方法 qSetMessagePattern来修改日志的输出格式。日志格式中常用的占位符号如下所示:

%{appname}     应用程序的名称(QCoreApplication::applicationName())
%{category}    日志所处的领域
%{file}        打印该日志的文件路径 
%{function}    打印日志的函数
%{line}        打印日志在文件中的行数
%{message}     日志的内容
%{pid}         打印日志的程序的PID(QCoreApplication::applicationPid())
%{threadid}    打印日志的线程ID
%{qthreadptr}  打印日志的线程指针
%{type}        日志级别("debug", "warning", "critical" or "fatal")
%{time process}日志发生时程序启动了多久
%{time boot}   日志发生时系统启动了多久
%{time [format]}以固定时间格式输出日志打印的时间,默认为QISODate格式

普通格式化

格式化日志方法如下:

#include <QApplication>
#include <QDebug>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss} [%{type}] %{file} [%{function}(%{line})]  %{message}");
    qInfo()<<"info";
    qDebug()<<"debug";
    qWarning()<<"warning";
    qCritical()<<"Critical";

    return a.exec();
}

输出的日志内容如下:

2024-09-19 10:35:25 [info] ..\..\main.cpp [qMain(9)]  info
2024-09-19 10:35:25 [debug] ..\..\main.cpp [qMain(10)]  debug
2024-09-19 10:35:25 [warning] ..\..\main.cpp [qMain(11)]  warning
2024-09-19 10:35:25 [critical] ..\..\main.cpp [qMain(12)]  Critical

注意:在Release模式下,文件名、函数名、行号获取不到,需要添加编译时宏QT_MESSAGELOGCONTEXT

条件格式化

在消息参数中还可以使用条件,给不同级别的日志指定不同的格式,语法如下:

%{if-<debug | info | warning | critical | fatal>} ... %{endif}

比如,只想在debug级别下输出文件名、函数名以及行号,代码格式如下:

qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss} [%{type}] %{if-debug}%{file}%{endif} %{if-} [%{function}(%{line})]  %{message}");

环境变量设置格式化

格式也可以在运行时通过设置QT_MESSAGE_PATTERN环境变量来更改;

如果调用了qSetMessagePattern()并且设置了QT_MESSAGE_PATTERN,则环境变量优先。

qputenv("QT_MESSAGE_PATTERN", QByteArray("%{time yyyy-MM-dd hh:mm:ss} [%{type}] %{file} [%{function}(%{line})]  %{message}"));

日志输出位置

Qt 默认的日志内容是输出到终端的,不会输出到文件里面,如果需要将日志内容输出到文件中,需要通过qInstallMessageHandler设置日志信息处理函数。使用方法如下:

#include <QApplication>
#include <QFile>
#include <QDebug>
#include <QTextStream>

// 日志消息的处理函数
void logMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
    // 获取格式化的日志信息
    QString typeStr = qFormatLogMessage(type, context, message);
    // 可以根据日志的级别进行过滤(比如不想要 Debug 输出,可以直接 return)
    QString levelText;
    switch (type)
    {
    case QtDebugMsg:
        levelText = "Debug";
        // return;	// 加上 return 之后就不会打印 debug 日志了
        break;
    case QtInfoMsg:
        levelText = "Info";
        break;
    case QtWarningMsg:
        levelText = "Warning";
        break;
    case QtCriticalMsg:
        levelText = "Critical";
        break;
    case QtFatalMsg:
        levelText = "Fatal";
        break;
    }
    QFile file("log.log");
    file.open(QIODevice::WriteOnly | QIODevice::Append);
    QTextStream textStream(&file);
    textStream << typeStr << "\n";
}

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss} [%{type}]%{if-warning}[%{function}]%{endif}%{if-fatal}[%{function}--%{line}]%{endif}:%{message}");
    qInstallMessageHandler(logMessageHandler);
    qDebug() << "debug info occurred";
    qInfo() << "call other function";
    qWarning() << "doesn't work";
    qFatal("fatal error");
    return app.exec();
}

如果需要关闭日志输出,取消之前注册的日志处理函数,可以调用qInstallMessageHandler(nullptr);

日志输出对象信息

在调试一些复杂对象的时候,需要输出对象的成员信息到日志当中。但是默认情况下 Qt 的日志库是不支持输出自定义对象的。这时候可以通过重写操作符实现对自定义对象的日志输出。使用方法如下:

class Person
{
public:
    QString name;
    quint8   age;
    
    inline friend QDebug operator<<(QDebug debug,const Person& person)
    {
        debug<<"Person("<<person.name<<","<<person.age<<")";
        return debug;
    }
};

int main(int argc,char*argv[])
{
    QCoreApplication app(argc, argv);

    Person person{"shayebushi",18};  // 输出:Person( "shayebushi" , 18 )
    qDebug()<<person;

    return app.exec();
}

禁用输出

在开发或者调试时,我们必须借助日志来进行判断,但是当程序需要发布时,调试的日志信息将不再需要,此时如果把代码删除,又不太方便,万一出bug了,又需要调试了呢?

所以Qt提供了禁用qInfoqWarningqDebug输出的宏,qCriticalqFatal是错误不能屏蔽!

CMakeLists.txt 文件中禁用日志输出

在CMakeLists.txt文件中添加如下三行,即可禁用qInfoqWarningqDebug的输出。

add_compile_definitions(QT_NO_INFO_OUTPUT)
add_compile_definitions(QT_NO_DEBUG_OUTPUT)
add_compile_definitions(QT_NO_WARNING_OUTPUT)
#或
add_compile_definitions(
    QT_NO_INFO_OUTPUT
    QT_NO_DEBUG_OUTPUT
    QT_NO_WARNING_OUTPUT)

.pro 文件中禁用日志输出

在 .pro 文件中添加如下三行,即可禁用 qInfoqWarningqDebug 的输出。

DEFINES += QT_NO_INFO_OUTPUT
DEFINES += QT_NO_DEBUG_OUTPUT
DEFINES += QT_NO_WARNING_OUTPUT

QsLog日志库

QsLog是一个基于Qt的QDebug类的易于使用的记录器。QsLog是在MIT许可下以开源形式发布的。

QsLog Github下载

QsLog的特征:

  • 六个日志级别(从跟踪到致命);
  • 运行时可配置的日志级别阈值;
  • 关闭日志记录时的最小开销;
  • 支持多个目标,附带文件和调试目标;
  • 线程安全;
  • 支持现成的常见Qt类型的日志记录;
  • 小依赖:直接把它放到你的项目中。

使用方法

1. 将QsLog目录添加到项目中

将QsLog目录直接拷贝到项目的根目录中,与CMakeLists.txt.pro文件同级。

QsLog目录

2. 配置CMakeLists.txt文件

打开项目的CMakeLists.txt文件,输入如下代码:

# 添加子目录
add_subdirectory(./QsLog)

# 添加可执行文件
add_executable(QsLog_test main.cpp)

# 把QsLog库链接到目标 QsLog_test
target_link_libraries(QsLog_test Qt${QT_VERSION_MAJOR}::Core QsLog)
3. 配置.pro文件

如果你使用的是qmake构建系统,打开项目的.pro文件,输入如下代码:

# 指定包含QsLog目录
include(QsLog/QsLog.pri)
4. 日志记录器的配置

在你的代码中进行日志记录器的配置:

#include <QCoreApplication>
#include "QsLog/QsLog.h"
#include "QsLog/QsLogDest.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    using namespace QsLogging;

    // 1. 获取日志单例
    Logger &logger = Logger::instance();
    logger.setLoggingLevel(QsLogging::DebugLevel);

    // 2. 添加两个日志输出目的地
    DestinationPtr file_dest(DestinationFactory::MakeFileDestination("log.txt"));
    DestinationPtr console_dest(DestinationFactory::MakeDebugOutputDestination());

    logger.addDestination(file_dest);
    logger.addDestination(console_dest);

    // 3. 开始日志记录
    QLOG_DEBUG() << "Program started";

    return a.exec();
}
5. 运行程序

运行程序,控制台会输出日志,同时指定的文件也会记录日志内容。日志内容如下:

DEBUG 2024-09-19T18:07:04.434 Program started 
6. 启用行号和文件名

输出的日志记录默认是没有文件名和行号的,如果需要,在CMakeLists.txt.pro文件中添加宏定义即可。

CMakeLists.txt
# 开启行号
add_compile_definitions(QS_LOG_LINE_NUMBERS)
.pro 文件
# 开启行号
DEFINES += QS_LOG_LINE_NUMBERS
7. 禁用日志输出

如果要禁用日志输出,定义宏 QS_LOG_DISABLE 即可。

CMakeLists.txt
# 禁用日志输出
add_compile_definitions(QS_LOG_DISABLE)
.pro 文件
# 禁用日志输出
DEFINES += QS_LOG_DISABLE
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐