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(®istryMutex);
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博客