qt 日志输出 QMessageLogger QDebug QLoggingCategory qDebug qt_message_output() qInstallMessageHandler()

qt的日志输出有 QMessageLogger、QDebug、QLoggingCategory三个类进行操作,qDebug 和qCDebug是为便捷使用那三个类对象而进行的宏包装。
另外qt还为外界提供 qt_message_output()  进行日志输出,以及qInstallMessageHandler进行输出控制操作。

一、QMessageLogger、QDebug、qDebug()、qt_message_output() 

qt日志输出简单案例:

#include "qloggingcategory.h"
#include <QDebug>

int main()
{
    char buffer[]="this is the message";
    //方式1。通过qDebug()宏输出
    qDebug()<<buffer<<endl;    //按C++的cout的样式输出信息。
    qDebug("%s",buffer);  //按C语言的printf()的样式输出信息。


    //方式2。通过QMessageLogger输出
    QMessageLogger logger(__FILE__,__LINE__,__FUNCTION__,"gg");
    logger.debug(buffer);//QtDebugMsg类型
    logger.info(buffer);//QtInfoMsg类型

    //方式3,通过QDebug输出
    {QDebug dbg=logger.debug();dbg<<buffer;}


    //方式4。通过qt_message_output进行输出
    qt_message_output(QtMsgType::QtDebugMsg,QMessageLogContext(__FILE__,__LINE__,
__FUNCTION__,"gg"),buffer);

    return 0;
}

qt默认输出是如何输出到 应用程序输出面板 (application output panel)

qt中,拥有专用的打印调试信息的QDebug类和QMessageLogger类,QMessageLogger是主要进行打印和对打印信息进行格式化编排的类。QDebug则为QMessageLogger提供类似cout样式的标准化输出操作。QDebug还可以用于向文件、字符串中进行流式输入的操作

qt为QMessageLogger类对象做了宏定义qDebug()包装,使得使用起来与C++中cout标准输入输出的用法一致,极为方便。
 qDebug() 、QDebug对象、QMessageLogger对象,默认会输出到qt creator 的 应用程序输出面板 (application output panel)
通过调试可知,qt中所有最终处理调试信息输出的方法都在qt源码的qlogging.cpp 文件中的qt_message_print()函数。比如QDebug对象在析构的时候就会调用其中的qt_message_output()函数 。

//D:\Qt\Qt5.12.0\5.12.0\Src\qtbase\src\corelib\io\qdebug.cpp
QDebug::~QDebug()
{
    if (!--stream->ref) {
        if (stream->space && stream->buffer.endsWith(QLatin1Char(' ')))
            stream->buffer.chop(1);
        if (stream->message_output) {
            qt_message_output(stream->type,
                              stream->context,
                              stream->buffer);
        }
        delete stream;
    }
}
--------------------------------------
//D:\Qt\Qt5.12.0\5.12.0\Src\qtbase\src\corelib\global\qlogging.h
​​​​​​​void qt_message_output(QtMsgType msgType, const QMessageLogContext &context, const QString &message)

qt在windows下qt_message_print()中最后会调用到windows的Api 函数:OutputDebugString().,这个函数会将字符串输出到 qt ide的  应用程序输出面板(application output panel​​​​​​​ )  上。

 如果开启了控制台窗口,可以通过设置qt环境变量QT_FORCE_STDERR_LOGGING值为1,可以强制内容输出到stderr上,也就是输出到控制台窗口上。

qt 输出到控制台窗口:vs qt 调试 输出 打印 到输出窗口 或控制台窗口-CSDN博客  
还可以对输出的日志进行格式化显示:qt 格式化打印 日志 QMessagePattern 

 二、QLoggingCategory、QLoggingRegistry(内部类)、QLoggingRule(内部类)

QLoggingCategory 定义日志类别,用于与两个内部类 QLoggingRegistry和QLoggingRule 共同完成灵活的可配置的日志级别输出。qt中默认有四个级别的日志,Debug、Info、Warning、Critical,依次Debug级别最低,Fatal级别最高。
QLoggingRegistry有一个全局静态对象,用于管理QLoggingCatogry,QLoggingRule用于解析日志显示的配置规则。每个QLoggingCategory对象初始化时都会

//D:\Qt\Qt5.12.0\5.12.0\Src\qtbase\src\corelib\io\qloggingcategory.cpp
QLoggingCategory::QLoggingCategory(const char *category, QtMsgType enableForLevel)
    : d(0),
      name(0)
{
    init(category, enableForLevel);
}

void QLoggingCategory::init(const char *category, QtMsgType severityLevel)
{
    enabled.store(0x01010101);   // enabledDebug = enabledWarning = enabledCritical = true;

    if (category)
        name = category;
    else
        name = qtDefaultCategoryName;

    if (QLoggingRegistry *reg = QLoggingRegistry::instance())
        reg->registerCategory(this, severityLevel);  //将QLoggingCategory对象注册到QRegisterCategory的全局静态对象中。
}

QLoggingCategory 的简单使用

让一个日志类别只在调用debug()时输出,则通过该类对象的setEnabled(QtDebugMsg,true)成员函数设置。需要注意的是通过QDebug进行输出时并不会检查category是否为true。
通过qSetMessagePattern()来设置当前每条输出的日志的样式: qt 格式化打印 日志  
下面是QLogginCategory的源码,QLogginCategory对象默认将Debug info warning critical 类别开关全部设置为true

#include "qloggingcategory.h"
#include <QDebug>

Q_LOGGING_CATEGORY(getGlobalCategory_gg,"gg.kk",QtMsgType::QtDebugMsg); 
//通过这句代码定义返回一个QLoggingCategory全局对象,函数名为getGlobalCategory_gg的函数,
//返回的QLoggingCategory对象中存放的名字为gg.kk

int main()
{
    QString pattern="%{if-category}%{category}:%{if-debug}Debug:%{endif}%{if-info}Info:%{endif}"\
    "%{if-warning}Waring:%{endif}%{if-critical}Crotical:%{endif}%{if-fatal}fatal%{endif}%{endif}"\
    "%{message}|%{file}{%{function}:%{line}}";

    qSetMessagePattern(pattern);  //设置日志格式

    char buffer[]="this is the message";

    QMessageLogger(__FILE__,__LINE__,__FUNCTION__).debug("gg","%s",buffer);

    //方式4。通过QLoggingCategory设置复合QtMsgType 的 category
    QLoggingCategory ggcat("gg1"); //定义一个名字为gg1的QLoggingCategory 对象
    ggcat.setFilterRules("");

    //通过qCDebug宏或qDebug宏,按标准输出printf()的样式输出信息。
    qCDebug(ggcat, "%s",buffer);
    ggcat.setEnabled(QtMsgType::QtDebugMsg,false);//单独关闭debug类型的日志
    qDebug(ggcat,"aaaa : %s",buffer);  //这条日志将不会显示
    ggcat.setEnabled(QtMsgType::QtDebugMsg,true); //打开debug类型的日志
    //按C++的cout的样式输出信息。
    qDebug(ggcat,buffer);
    qInfo(ggcat,buffer);
    qWarning(ggcat,buffer);

    //通过函数getGlobalCategory_gg获取全局的QLoggingCategory对象。
    qDebug(getGlobalCategory_gg,buffer);

    return 0;
}

QLoggingRegistry(内部类)、QLoggingRule(内部类)与 灵活的日志输出配置

QLoggingRegistry类的全局对象管理所有的category,在第一次调用的时候会初始化对象,并且在初始化的时候就通过 QLoggerRule对象 加载所有的配置规则,每次增加category对象或者通过category对象调用setApiRules()时都会做一次 将所有rule应用到所有的category上 的操作。
可以通过四种方式设置rule
第一种,通过当前工程的配置文件设置,下面是qt5.12中文档中介绍的,不同平台配置文件的路径:

on macOS and iOS: ~/Library/Preferences
on Unix: ~/.config, /etc/xdg
on Windows: %LOCALAPPDATA%, %ProgramData%, QCoreApplication::applicationDirPath()+/QtProject/qtlogging.ini, QCoreApplication::applicationDirPath() + QLibraryInfo::location(QLibraryInfo::LibraryLocation::DataPath)+qtlogging.ini

设置qtlogging.ini配置文件有两种方式:
1、在程序编译出来的可执行文件中同级文件夹下创建QtProject的文件夹,然后将qtlogging.ini文件放进去。
2、在程序编译出来的可执行文件中同级文件夹下放入qt.conf,设置Data的路径,并将qtlogging.ini文件放进去。QCoreApplication对象初始化的时候,会自动加载qt.conf

;一个简单的配置文件,必须要将文件名字保存为  QtProject/qtlogging.ini
; ";"作为行的开头,表示这行是注释
;[Rules]必须要写的,否则不会解析后面的内容。
[Rules]

;控制所有名字的category的debug级别日志关闭 '*' 只支持在category名称前面或后面,不能在名称中间进行模糊查找。
*.debug=false

;控制名字为driver.usb的category的debug级别日志开启
driver.usb.debug=true
;qt.conf
[Paths]
Documentation=../../Docs/Qt-5.12.0
Examples=../../Examples/Qt-5.12.0
Prefix=.
Data=.

第二种是通过设置qt环境变量, QT_LOGGING_RULES="*.debug=false;driver.usb.debug=true"

第三种是通过QCategoryMessage类的静态成员函数setFilterRules(QString &rules)进行设置。

QLoggingCategory::setFilterRules("*.debug=false\ndriver.usb.debug=true");

第四种,是通过在qt库所在的文件夹下放入qtlogging.ini文件。

qt提供日志按级别输出的规则。通过QLoggerRule对配置进行解析获取到规则。

四种配置方式对应全局的QLoggingRegistry类对象管理的四种ruleset。

D:\Qt\Qt5.12.0\5.12.0\Src\qtbase\src\corelib\io\qloggingregistry_p.h    
.......... 
    enum RuleSet {
        // sorted by order in which defaultCategoryFilter considers them:
        QtConfigRules,
        ConfigRules,
        ApiRules,
        EnvironmentRules,

        NumRuleSets
    };
.........


D:\Qt\Qt5.12.0\5.12.0\Src\qtbase\src\corelib\io\qloggingregistry.cpp
..........
    ruleSets[EnvironmentRules] = std::move(er);
    ruleSets[QtConfigRules] = std::move(qr);
    ruleSets[ConfigRules] = std::move(cr);
..........

void QLoggingRegistry::setApiRules(const QString &content)
{
    QLoggingSettingsParser parser;
    parser.setImplicitRulesSection(true);
    parser.setContent(content);

    if (qtLoggingDebug())
        debugMsg("Loading logging rules set by QLoggingCategory::setFilterRules ...");

    const QMutexLocker locker(&registryMutex);

    ruleSets[ApiRules] = parser.rules(); //+++++++++++++++++++

    updateRules();
}

需要注意的是,QDebug的输出和QMessageLogger中不带QMessageCategory对象或QMessageCategory::CategoryFunction对象的参数的输出函数以及qt_message_output函数,是不受rule的影响的,会直接输出。

四、qInstallMessageHandler函数

下面是qDefaultMessageHandler函数,qt中QMessageLogger、QDebug、qDebug()、qt_message_output() 都是通过调用这个入口函数,而调用该函数的上一级是qt_message_print()函数。

//D:\Qt\Qt5.12.0\5.12.0\Src\qtbase\src\corelib\global\qlogging.cpp
static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context,
                                   const QString &message)
{
    bool handledStderr = false;

    // A message sink logs the message to a structured or unstructured destination,
    // optionally formatting the message if the latter, and returns true if the sink
    // handled stderr output as well, which will shortcut our default stderr output.
    // In the future, if we allow multiple/dynamic sinks, this will be iterating
    // a list of sinks.

#if !defined(QT_BOOTSTRAPPED)
# if defined(Q_OS_WIN)
    handledStderr |= win_message_handler(type, context, message);
# elif QT_CONFIG(slog2)
    handledStderr |= slog2_default_handler(type, context, message);
# elif QT_CONFIG(journald)
    handledStderr |= systemd_default_message_handler(type, context, message);
# elif QT_CONFIG(syslog)
    handledStderr |= syslog_default_message_handler(type, context, message);
# elif defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
    handledStderr |= android_default_message_handler(type, context, message);
# elif defined(QT_USE_APPLE_UNIFIED_LOGGING)
    handledStderr |= AppleUnifiedLogger::messageHandler(type, context, message);
# elif defined Q_OS_WASM
    handledStderr |= wasm_default_message_handler(type, context, message);
# endif
#endif

    if (!handledStderr)
        stderr_message_handler(type, context, message);
}

#ifdef Q_OS_WIN
static bool win_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
    if (shouldLogToStderr())
        return false; // Leave logging up to stderr handler

    QString formattedMessage = qFormatLogMessage(type, context, message);
    formattedMessage.append(QLatin1Char('\n'));
    OutputDebugString(reinterpret_cast<const wchar_t *>(formattedMessage.utf16()));

    return true; // Prevent further output to stderr
}
#endif

bool shouldLogToStderr()
{
    static bool forceStderrLogging = qEnvironmentVariableIntValue("QT_FORCE_STDERR_LOGGING");
    return forceStderrLogging || stderrHasConsoleAttached();
}


static void qt_message_print(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
{
#ifndef QT_BOOTSTRAPPED
    // qDebug, qWarning, ... macros do not check whether category is enabled
    if (isDefaultCategory(context.category)) {
        if (QLoggingCategory *defaultCategory = QLoggingCategory::defaultCategory()) {
            if (!defaultCategory->isEnabled(msgType))
                return;
        }
    }
#endif

    // prevent recursion in case the message handler generates messages
    // itself, e.g. by using Qt API
    if (grabMessageHandler()) {
        // prefer new message handler over the old one
        if (msgHandler.load() == qDefaultMsgHandler
                || messageHandler.load() != qDefaultMessageHandler) {
            (*messageHandler.load())(msgType, context, message);
        } else {
            (*msgHandler.load())(msgType, message.toLocal8Bit().constData());
        }
        ungrabMessageHandler();
    } else {
        fprintf(stderr, "%s\n", message.toLocal8Bit().constData());
    }
}

自定义messageHandler,这种方式QMessagePattern 会失效,但是QMessageCategory和rule还是有效的。
下面是自定义messageHandler的案例。

#include <QDebug>

void myMessageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
   QByteArray localMsg = msg.toLocal8Bit();
   switch (type) {
   case QtDebugMsg:
       fprintf(stderr, "debug:%s\n",msg.toUtf8().data());
       break;
   case QtInfoMsg:
       fprintf(stderr, "info:%s\n",msg.toUtf8().data());
       break;
   case QtWarningMsg:
       fprintf(stderr, "warning:%s\n",msg.toUtf8().data());
       break;
   case QtCriticalMsg:
       fprintf(stderr, "critical:%s\n",msg.toUtf8().data());
       break;
   case QtFatalMsg:
       fprintf(stderr, "fatal:%s\n",msg.toUtf8().data());
       abort();
   }
}

// 程序代码
int main(int argc, char *argv[])
{
   qInstallMessageHandler(myMessageOutput);

   qDebug()<<"test"<<endl;
   return 0;
}

qt 汉字输出 中文输出 显示乱码 qDebug() 乱码 解决-CSDN博客 
QString 与 字符编码 QTextCodec-CSDN博客 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值