Qt中debug和release状态下输出内容有所不同,debug模式下包含了数百条警告及错误日志,除非在编译期间设置了QT_NO_WARNING_OUTPUT和/或QT_NO_DEBUG_OUTPUT,否则在发布模式下构建的Qt也包含此类警告。
qt 格式化输出内容有两种方法: qInstallMessageHandler 和 qSetMessagePattern
qSetMessagePattern 使用方法
Placeholder |
Description |
%{appname} |
QCoreApplication::applicationName() |
%{category} |
Logging category |
%{file} |
Path to source file |
%{function} |
Function |
%{line} |
Line in source file |
%{message} |
The actual message |
%{pid} |
QCoreApplication::applicationPid() |
%{threadid} |
The system-wide ID of current thread (if it can be obtained) |
%{qthreadptr} |
A pointer to the current QThread (result of QThread::currentThread()) |
%{type} |
"debug", "warning", "critical" or "fatal" |
%{time process} |
time of the message, in seconds since the process started (the token "process" is literal) |
%{time boot} |
the time of the message, in seconds since the system boot if that can be determined (the token "boot" is literal). If the time since boot could not be obtained, the output is indeterminate (see QElapsedTimer::msecsSinceReference()). |
%{backtrace [depth=N] [separator="..."]} |
A backtrace with the number of frames specified by the optional depth parameter (defaults to 5), and separated by the optional separator parameter (defaults to "|"). This expansion is available only on some platforms (currently only platfoms using glibc). Names are only known for exported functions. If you want to see the name of every function in your application, use QMAKE_LFLAGS += -rdynamic. When reading backtraces, take into account that frames might be missing due to inlining or tail call optimization. |
QT_MESSAGE_PATTERN="[%{time yyyyMMdd h:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{file}:%{line} - %{message}"
#define QT_MESSAGE_PATTERN="[%{time yyyyMMdd h:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{file}:%{line} - %{message}"
int main(int argc, char *argv[])
{
qSetMessagePattern(QT_MESSAGE_PATTERN);
QApplication a(argc, argv);
QString str1 = QString("%1/%2/%3/%4").arg(1).arg(2).arg(3).arg(4);
qDebug() << str1;
return a.exec();
}
输出结果: Debug:[20190611 21:12:15.517 中国标准时间Deww] ..\testListCrash\main.cpp:96 - "1/2/3/4"
Release下: [20190611 21:10:32.516 中国标准时间Deww] unknown:0 - "1/2/3/4"
输出结果不同,因为release模式下 系统默认使用了 QT_NO_DEBUG_OUTPUT的宏定义,导致输出时获取不到 file function line的内容。 解决: qt工程 在.pro文件中添加 DEFINES += QT_MESSAGELOGCONTEXT , vs工程中 属性--C++预处理器 ,预处理器中删除 QT_NO_DEBUG_OUTPUT,添加 QT_MESSAGELOGCONTEXT 。工程修改好后重新输出,release下也能听获取详细信息。
日志输出到指定文件中:
在main中添加 qInstallMessageHandler(myMessageOutput) 方法捕获消息内容, myMessageOutput 中使用 qFormatLogMessage 获取消息内容
qInstallMessageHandler
qt中定义的日志消息捕获器,能够重新格式化输出日志内容,使用方法:
void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
QByteArray localMsg = msg.toLocal8Bit();
switch (type) {
case QtDebugMsg:
fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtInfoMsg:
fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtWarningMsg:
fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtCriticalMsg:
fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
break;
case QtFatalMsg:
fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
abort();
}
}
int main(int argc, char *argv[])
{
qInstallMessageHandler(myMessageOutput);
QApplication app(argc, argv);
qDebug() << "hello";
return app.exec();
}
输出 Debug模式下 hello, line- 79, release 模式下 hello, line- 0 ,获取行数失败,原因同上,需要在 pro 或者vs工程文件中添加 QT_MESSAGELOGCONTEXT 的宏定义。添加完成后,输出正常。
上面的日志格式设置好后,在运行qt工程时可以输出到控制台中,需要将 myMessageOutput 中的内容缓存写到文件中。
QFile *m_file;
m_file = new QFile(1.txt“”);
if (!m_file->open(QIODevice::WriteOnly)) {
delete m_file;
m_file = NULL;
return;
}
m_file->write(stderr, size);
m_file->flush();
qt中提供5种级别日志,qDebug qInfo qWarning qCritical qFatal 是并列的日志级别, 以下只以 qDebug的源码进行分析,其他的类似
定义:
#define qDebug QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug
qDebug() << "hello";
qDebug("hello %s", str);
以上是qDebug的两种调用方式, 传可变参数和使用 <<
传可变参数时,可变参数的原理及介绍参考 https://blog.csdn.net/youwuwei2012/article/details/28239089 文档。
void QMessageLogger::debug(const char *msg, ...) const
{
va_list ap;
va_start(ap, msg); // use variable arg list
const QString message = qt_message(QtDebugMsg, context, msg, ap); //根据不同的消息级别
// 分 , 如qWarning 传
//QtWarningMsg
va_end(ap);
if (isFatal(QtDebugMsg))
qt_message_fatal(QtDebugMsg, context, message);
}
static QString qt_message(QtMsgType msgType, const QMessageLogContext &context, const char *msg, va_list ap)
{
QString buf = QString::vasprintf(msg, ap); //分割参数,输出 QString 格式
qt_message_print(msgType, context, buf);
return buf;
}
static void qt_message_print(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
{
...
(*messageHandler.load())(msgType, context, message); //前面定义的消息捕获回调函数
//qInstallMessageHandler(QtMessageOutput)
...
}
qDebug() << 的使用原理如下
// 构造 QDebug 类的对象,拷贝内容,返回 QDebug 对象
QDebug QMessageLogger::debug() const
{
QDebug dbg = QDebug(QtDebugMsg);
QMessageLogContext &ctxt = dbg.stream->context;
ctxt.copy(context);
return dbg;
}
//追加内容
inline QDebug &operator<<(signed int t) { stream->ts << t; return maybeSpace(); }
那么 什么时候将内容输出呢?析构函数中输出了
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;
}
}