diff options
Diffstat (limited to 'src/corelib/global/qlogging.cpp')
-rw-r--r-- | src/corelib/global/qlogging.cpp | 1238 |
1 files changed, 852 insertions, 386 deletions
diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp index e562fed336..71579ca08a 100644 --- a/src/corelib/global/qlogging.cpp +++ b/src/corelib/global/qlogging.cpp @@ -1,43 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com> -** Copyright (C) 2018 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com> +// Copyright (C) 2022 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qglobal_p.h" #include "qlogging.h" @@ -59,7 +23,6 @@ #include "qthread.h" #include "private/qloggingregistry_p.h" #include "private/qcoreapplication_p.h" -#include "private/qsimd_p.h" #include <qtcore_tracepoints_p.h> #endif #ifdef Q_OS_WIN @@ -107,14 +70,14 @@ extern char *__progname; #endif #ifndef QT_BOOTSTRAPPED -#if QT_CONFIG(regularexpression) -# ifdef __UCLIBC__ -# if __UCLIBC_HAS_BACKTRACE__ -# define QLOGGING_HAVE_BACKTRACE -# endif -# elif (defined(__GLIBC__) && defined(__GLIBCXX__)) || (__has_include(<cxxabi.h>) && __has_include(<execinfo.h>)) -# define QLOGGING_HAVE_BACKTRACE +#if __has_include(<cxxabi.h>) && QT_CONFIG(backtrace) && QT_CONFIG(regularexpression) +# include <qregularexpression.h> +# if QT_CONFIG(dladdr) +# include <dlfcn.h> # endif +# include BACKTRACE_HEADER +# include <cxxabi.h> +# define QLOGGING_HAVE_BACKTRACE #endif #if defined(Q_OS_LINUX) && (defined(__GLIBC__) || __has_include(<sys/syscall.h>)) @@ -152,12 +115,6 @@ static QT_PREPEND_NAMESPACE(qint64) qt_gettid() return qintptr(QThread::currentThreadId()); } #endif - -#ifdef QLOGGING_HAVE_BACKTRACE -# include <qregularexpression.h> -# include <cxxabi.h> -# include <execinfo.h> -#endif #endif // !QT_BOOTSTRAPPED #include <cstdlib> @@ -169,12 +126,51 @@ static QT_PREPEND_NAMESPACE(qint64) qt_gettid() QT_BEGIN_NAMESPACE -#if !defined(Q_CC_MSVC) +using namespace Qt::StringLiterals; + +#ifndef QT_BOOTSTRAPPED +Q_TRACE_POINT(qtcore, qt_message_print, int type, const char *category, const char *function, const char *file, int line, const QString &message); +#endif + +/*! + \headerfile <QtLogging> + \inmodule QtCore + \title Qt Logging Types + + \brief The <QtLogging> header file defines Qt logging types, functions + and macros. + + The <QtLogging> header file contains several types, functions and + macros for logging. + + The QtMsgType enum identifies the various messages that can be generated + and sent to a Qt message handler; QtMessageHandler is a type definition for + a pointer to a function with the signature + \c {void myMessageHandler(QtMsgType, const QMessageLogContext &, const char *)}. + qInstallMessageHandler() function can be used to install the given + QtMessageHandler. QMessageLogContext class contains the line, file, and + function the message was logged at. This information is created by the + QMessageLogger class. + + <QtLogging> also contains functions that generate messages from the + given string argument: qDebug(), qInfo(), qWarning(), qCritical(), + and qFatal(). These functions call the message handler + with the given message. + + Example: + + \snippet code/src_corelib_global_qglobal.cpp 4 +*/ + +template <typename String> +#if !defined(Q_CC_MSVC_ONLY) Q_NORETURN #endif -static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, const QString &message); +static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, String &&message); static void qt_message_print(QtMsgType, const QMessageLogContext &context, const QString &message); -static void qt_message_print(const QString &message); +static void preformattedMessageHandler(QtMsgType type, const QMessageLogContext &context, + const QString &formattedMessage); +static QString formatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str); static int checked_var_value(const char *varname) { @@ -190,6 +186,17 @@ static int checked_var_value(const char *varname) return ok ? value : 1; } +static bool is_fatal_count_down(QAtomicInt &n) +{ + // it's fatal if the current value is exactly 1, + // otherwise decrement if it's non-zero + + int v = n.loadRelaxed(); + while (v != 0 && !n.testAndSetRelaxed(v, v - 1, v)) + qYieldCpu(); + return v == 1; // we exited the loop, so either v == 0 or CAS succeeded to set n from v to v-1 +} + static bool isFatal(QtMsgType msgType) { if (msgType == QtFatalMsg) @@ -197,28 +204,17 @@ static bool isFatal(QtMsgType msgType) if (msgType == QtCriticalMsg) { static QAtomicInt fatalCriticals = checked_var_value("QT_FATAL_CRITICALS"); - - // it's fatal if the current value is exactly 1, - // otherwise decrement if it's non-zero - return fatalCriticals.loadRelaxed() && fatalCriticals.fetchAndAddRelaxed(-1) == 1; + return is_fatal_count_down(fatalCriticals); } if (msgType == QtWarningMsg || msgType == QtCriticalMsg) { static QAtomicInt fatalWarnings = checked_var_value("QT_FATAL_WARNINGS"); - - // it's fatal if the current value is exactly 1, - // otherwise decrement if it's non-zero - return fatalWarnings.loadRelaxed() && fatalWarnings.fetchAndAddRelaxed(-1) == 1; + return is_fatal_count_down(fatalWarnings); } return false; } -static bool isDefaultCategory(const char *category) -{ - return !category || strcmp(category, "default") == 0; -} - /*! Returns true if writing to \c stderr is supported. @@ -230,6 +226,8 @@ static bool systemHasStderr() return true; } +#ifndef Q_OS_WASM + /*! Returns true if writing to \c stderr will end up in a console/terminal visible to the user. @@ -314,6 +312,8 @@ bool shouldLogToStderr() using namespace QtPrivate; +#endif // ifndef Q_OS_WASM + /*! \class QMessageLogContext \inmodule QtCore @@ -348,7 +348,7 @@ using namespace QtPrivate; \sa QMessageLogContext, qDebug(), qInfo(), qWarning(), qCritical(), qFatal() */ -#if defined(Q_CC_MSVC) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR) +#if defined(Q_CC_MSVC_ONLY) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR) static inline void convert_to_wchar_t_elided(wchar_t *d, size_t space, const char *s) noexcept { size_t len = qstrlen(s); @@ -369,14 +369,15 @@ static inline void convert_to_wchar_t_elided(wchar_t *d, size_t space, const cha \internal */ Q_NEVER_INLINE -static QString qt_message(QtMsgType msgType, const QMessageLogContext &context, const char *msg, va_list ap) +static void qt_message(QtMsgType msgType, const QMessageLogContext &context, const char *msg, va_list ap) { QString buf = QString::vasprintf(msg, ap); qt_message_print(msgType, context, buf); - return buf; + + if (isFatal(msgType)) + qt_message_fatal(msgType, context, buf); } -#undef qDebug /*! Logs a debug message specified with format \a msg. Additional parameters, specified by \a msg, may be used. @@ -385,17 +386,13 @@ static QString qt_message(QtMsgType msgType, const QMessageLogContext &context, */ void QMessageLogger::debug(const char *msg, ...) const { + QInternalMessageLogContext ctxt(context); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtDebugMsg, context, msg, ap); + qt_message(QtDebugMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtDebugMsg)) - qt_message_fatal(QtDebugMsg, context, message); } - -#undef qInfo /*! Logs an informational message specified with format \a msg. Additional parameters, specified by \a msg, may be used. @@ -405,13 +402,11 @@ void QMessageLogger::debug(const char *msg, ...) const */ void QMessageLogger::info(const char *msg, ...) const { + QInternalMessageLogContext ctxt(context); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtInfoMsg, context, msg, ap); + qt_message(QtInfoMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtInfoMsg)) - qt_message_fatal(QtInfoMsg, context, message); } /*! @@ -422,8 +417,8 @@ void QMessageLogger::info(const char *msg, ...) const \snippet code/qlogging/qlogging.cpp 2 - A function which this signature is generated by Q_DECLARE_LOGGING_CATEGORY, - Q_LOGGING_CATEGORY. + The \c Q_DECLARE_LOGGING_CATEGORY macro generates a function declaration + with this signature, and \c Q_LOGGING_CATEGORY generates its definition. \since 5.3 */ @@ -440,17 +435,12 @@ void QMessageLogger::debug(const QLoggingCategory &cat, const char *msg, ...) co if (!cat.isDebugEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtDebugMsg, ctxt, msg, ap); + qt_message(QtDebugMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtDebugMsg)) - qt_message_fatal(QtDebugMsg, ctxt, message); } /*! @@ -467,17 +457,12 @@ void QMessageLogger::debug(QMessageLogger::CategoryFunction catFunc, if (!cat.isDebugEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtDebugMsg, ctxt, msg, ap); + qt_message(QtDebugMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtDebugMsg)) - qt_message_fatal(QtDebugMsg, ctxt, message); } #ifndef QT_NO_DEBUG_STREAM @@ -551,17 +536,12 @@ void QMessageLogger::info(const QLoggingCategory &cat, const char *msg, ...) con if (!cat.isInfoEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtInfoMsg, ctxt, msg, ap); + qt_message(QtInfoMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtInfoMsg)) - qt_message_fatal(QtInfoMsg, ctxt, message); } /*! @@ -578,17 +558,12 @@ void QMessageLogger::info(QMessageLogger::CategoryFunction catFunc, if (!cat.isInfoEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtInfoMsg, ctxt, msg, ap); + qt_message(QtInfoMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtInfoMsg)) - qt_message_fatal(QtInfoMsg, ctxt, message); } #ifndef QT_NO_DEBUG_STREAM @@ -639,7 +614,6 @@ QDebug QMessageLogger::info(QMessageLogger::CategoryFunction catFunc) const #endif -#undef qWarning /*! Logs a warning message specified with format \a msg. Additional parameters, specified by \a msg, may be used. @@ -648,13 +622,11 @@ QDebug QMessageLogger::info(QMessageLogger::CategoryFunction catFunc) const */ void QMessageLogger::warning(const char *msg, ...) const { + QInternalMessageLogContext ctxt(context); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtWarningMsg, context, msg, ap); + qt_message(QtWarningMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtWarningMsg)) - qt_message_fatal(QtWarningMsg, context, message); } /*! @@ -669,17 +641,12 @@ void QMessageLogger::warning(const QLoggingCategory &cat, const char *msg, ...) if (!cat.isWarningEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtWarningMsg, ctxt, msg, ap); + qt_message(QtWarningMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtWarningMsg)) - qt_message_fatal(QtWarningMsg, ctxt, message); } /*! @@ -696,17 +663,12 @@ void QMessageLogger::warning(QMessageLogger::CategoryFunction catFunc, if (!cat.isWarningEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtWarningMsg, ctxt, msg, ap); + qt_message(QtWarningMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtWarningMsg)) - qt_message_fatal(QtWarningMsg, ctxt, message); } #ifndef QT_NO_DEBUG_STREAM @@ -754,8 +716,6 @@ QDebug QMessageLogger::warning(QMessageLogger::CategoryFunction catFunc) const #endif -#undef qCritical - /*! Logs a critical message specified with format \a msg. Additional parameters, specified by \a msg, may be used. @@ -764,13 +724,11 @@ QDebug QMessageLogger::warning(QMessageLogger::CategoryFunction catFunc) const */ void QMessageLogger::critical(const char *msg, ...) const { + QInternalMessageLogContext ctxt(context); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtCriticalMsg, context, msg, ap); + qt_message(QtCriticalMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtCriticalMsg)) - qt_message_fatal(QtCriticalMsg, context, message); } /*! @@ -785,17 +743,12 @@ void QMessageLogger::critical(const QLoggingCategory &cat, const char *msg, ...) if (!cat.isCriticalEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtCriticalMsg, ctxt, msg, ap); + qt_message(QtCriticalMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtCriticalMsg)) - qt_message_fatal(QtCriticalMsg, ctxt, message); } /*! @@ -812,17 +765,12 @@ void QMessageLogger::critical(QMessageLogger::CategoryFunction catFunc, if (!cat.isCriticalEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtCriticalMsg, ctxt, msg, ap); + qt_message(QtCriticalMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtCriticalMsg)) - qt_message_fatal(QtCriticalMsg, ctxt, message); } #ifndef QT_NO_DEBUG_STREAM @@ -871,7 +819,51 @@ QDebug QMessageLogger::critical(QMessageLogger::CategoryFunction catFunc) const #endif -#undef qFatal +/*! + Logs a fatal message specified with format \a msg for the context \a cat. + Additional parameters, specified by \a msg, may be used. + + \since 6.5 + \sa qCFatal() +*/ +void QMessageLogger::fatal(const QLoggingCategory &cat, const char *msg, ...) const noexcept +{ + QInternalMessageLogContext ctxt(context, cat()); + + va_list ap; + va_start(ap, msg); // use variable arg list + QT_TERMINATE_ON_EXCEPTION(qt_message(QtFatalMsg, ctxt, msg, ap)); + va_end(ap); + +#ifndef Q_CC_MSVC_ONLY + Q_UNREACHABLE(); +#endif +} + +/*! + Logs a fatal message specified with format \a msg for the context returned + by \a catFunc. Additional parameters, specified by \a msg, may be used. + + \since 6.5 + \sa qCFatal() +*/ +void QMessageLogger::fatal(QMessageLogger::CategoryFunction catFunc, + const char *msg, ...) const noexcept +{ + const QLoggingCategory &cat = (*catFunc)(); + + QInternalMessageLogContext ctxt(context, cat()); + + va_list ap; + va_start(ap, msg); // use variable arg list + QT_TERMINATE_ON_EXCEPTION(qt_message(QtFatalMsg, ctxt, msg, ap)); + va_end(ap); + +#ifndef Q_CC_MSVC_ONLY + Q_UNREACHABLE(); +#endif +} + /*! Logs a fatal message specified with format \a msg. Additional parameters, specified by \a msg, may be used. @@ -880,14 +872,66 @@ QDebug QMessageLogger::critical(QMessageLogger::CategoryFunction catFunc) const */ void QMessageLogger::fatal(const char *msg, ...) const noexcept { - QString message; - + QInternalMessageLogContext ctxt(context); va_list ap; va_start(ap, msg); // use variable arg list - QT_TERMINATE_ON_EXCEPTION(message = qt_message(QtFatalMsg, context, msg, ap)); + QT_TERMINATE_ON_EXCEPTION(qt_message(QtFatalMsg, ctxt, msg, ap)); va_end(ap); - qt_message_fatal(QtFatalMsg, context, message); +#ifndef Q_CC_MSVC_ONLY + Q_UNREACHABLE(); +#endif +} + +#ifndef QT_NO_DEBUG_STREAM +/*! + Logs a fatal message using a QDebug stream. + + \since 6.5 + + \sa qFatal(), QDebug +*/ +QDebug QMessageLogger::fatal() const +{ + QDebug dbg = QDebug(QtFatalMsg); + QMessageLogContext &ctxt = dbg.stream->context; + ctxt.copyContextFrom(context); + return dbg; +} + +/*! + Logs a fatal message into category \a cat using a QDebug stream. + + \since 6.5 + \sa qCFatal(), QDebug +*/ +QDebug QMessageLogger::fatal(const QLoggingCategory &cat) const +{ + QDebug dbg = QDebug(QtFatalMsg); + + QMessageLogContext &ctxt = dbg.stream->context; + ctxt.copyContextFrom(context); + ctxt.category = cat.categoryName(); + + return dbg; +} + +/*! + Logs a fatal message into category returned by \a catFunc using a QDebug stream. + + \since 6.5 + \sa qCFatal(), QDebug +*/ +QDebug QMessageLogger::fatal(QMessageLogger::CategoryFunction catFunc) const +{ + return fatal((*catFunc)()); +} +#endif // QT_NO_DEBUG_STREAM + +#if !defined(QT_BOOTSTRAPPED) +static bool isDefaultCategory(const char *category) +{ + return !category || strcmp(category, "default") == 0; } /*! @@ -902,15 +946,20 @@ Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info) if (info.isEmpty()) return info; - int pos; + qsizetype pos; // Skip trailing [with XXX] for templates (gcc), but make // sure to not affect Objective-C message names. pos = info.size() - 1; if (info.endsWith(']') && !(info.startsWith('+') || info.startsWith('-'))) { while (--pos) { - if (info.at(pos) == '[') - info.truncate(pos); + if (info.at(pos) == '[') { + info.truncate(pos); + break; + } + } + if (info.endsWith(' ')) { + info.chop(1); } } @@ -924,14 +973,21 @@ Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info) // canonize operator names info.replace("operator ", "operator"); + pos = -1; // remove argument list forever { int parencount = 0; - pos = info.lastIndexOf(')'); + pos = info.lastIndexOf(')', pos); if (pos == -1) { // Don't know how to parse this function name return info; } + if (info.indexOf('>', pos) != -1 + || info.indexOf(':', pos) != -1) { + // that wasn't the function argument list. + --pos; + continue; + } // find the beginning of the argument list --pos; @@ -949,7 +1005,7 @@ Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info) info.truncate(++pos); if (info.at(pos - 1) == ')') { - if (info.indexOf(operator_call) == pos - (int)strlen(operator_call)) + if (info.indexOf(operator_call) == pos - qsizetype(strlen(operator_call))) break; // this function returns a pointer to a function @@ -972,19 +1028,19 @@ Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info) if (pos > -1) { switch (info.at(pos)) { case ')': - if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1) + if (info.indexOf(operator_call) == pos - qsizetype(strlen(operator_call)) + 1) pos -= 2; break; case '<': - if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1) + if (info.indexOf(operator_lessThan) == pos - qsizetype(strlen(operator_lessThan)) + 1) --pos; break; case '>': - if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1) + if (info.indexOf(operator_greaterThan) == pos - qsizetype(strlen(operator_greaterThan)) + 1) --pos; break; case '=': { - int operatorLength = (int)strlen(operator_lessThanEqual); + auto operatorLength = qsizetype(strlen(operator_lessThanEqual)); if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1) pos -= 2; else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1) @@ -1028,7 +1084,7 @@ Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info) break; // find the matching close - int end = pos; + qsizetype end = pos; templatecount = 1; --pos; while (pos && templatecount) { @@ -1091,6 +1147,7 @@ struct QMessagePattern int backtraceDepth; }; QList<BacktraceParams> backtraceArgs; // backtrace arguments in sequence of %{backtrace + int maxBacktraceDepth = 0; #endif bool fromEnvironment; @@ -1100,7 +1157,7 @@ struct QMessagePattern Q_DECLARE_TYPEINFO(QMessagePattern::BacktraceParams, Q_RELOCATABLE_TYPE); #endif -QBasicMutex QMessagePattern::mutex; +Q_CONSTINIT QBasicMutex QMessagePattern::mutex; QMessagePattern::QMessagePattern() { @@ -1109,7 +1166,7 @@ QMessagePattern::QMessagePattern() #endif const QString envPattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN")); if (envPattern.isEmpty()) { - setPattern(QLatin1String(defaultPattern)); + setPattern(QLatin1StringView(defaultPattern)); fromEnvironment = false; } else { setPattern(envPattern); @@ -1124,6 +1181,7 @@ void QMessagePattern::setPattern(const QString &pattern) timeArgs.clear(); #ifdef QLOGGING_HAVE_BACKTRACE backtraceArgs.clear(); + maxBacktraceDepth = 0; #endif // scanner @@ -1132,10 +1190,9 @@ void QMessagePattern::setPattern(const QString &pattern) bool inPlaceholder = false; for (int i = 0; i < pattern.size(); ++i) { const QChar c = pattern.at(i); - if ((c == QLatin1Char('%')) - && !inPlaceholder) { + if (c == u'%' && !inPlaceholder) { if ((i + 1 < pattern.size()) - && pattern.at(i + 1) == QLatin1Char('{')) { + && pattern.at(i + 1) == u'{') { // beginning of placeholder if (!lexeme.isEmpty()) { lexemes.append(lexeme); @@ -1147,7 +1204,7 @@ void QMessagePattern::setPattern(const QString &pattern) lexeme.append(c); - if ((c == QLatin1Char('}') && inPlaceholder)) { + if (c == u'}' && inPlaceholder) { // end of placeholder lexemes.append(lexeme); lexeme.clear(); @@ -1168,37 +1225,36 @@ void QMessagePattern::setPattern(const QString &pattern) for (int i = 0; i < lexemes.size(); ++i) { const QString lexeme = lexemes.at(i); - if (lexeme.startsWith(QLatin1String("%{")) - && lexeme.endsWith(QLatin1Char('}'))) { + if (lexeme.startsWith("%{"_L1) && lexeme.endsWith(u'}')) { // placeholder - if (lexeme == QLatin1String(typeTokenC)) { + if (lexeme == QLatin1StringView(typeTokenC)) { tokens[i] = typeTokenC; - } else if (lexeme == QLatin1String(categoryTokenC)) + } else if (lexeme == QLatin1StringView(categoryTokenC)) tokens[i] = categoryTokenC; - else if (lexeme == QLatin1String(messageTokenC)) + else if (lexeme == QLatin1StringView(messageTokenC)) tokens[i] = messageTokenC; - else if (lexeme == QLatin1String(fileTokenC)) + else if (lexeme == QLatin1StringView(fileTokenC)) tokens[i] = fileTokenC; - else if (lexeme == QLatin1String(lineTokenC)) + else if (lexeme == QLatin1StringView(lineTokenC)) tokens[i] = lineTokenC; - else if (lexeme == QLatin1String(functionTokenC)) + else if (lexeme == QLatin1StringView(functionTokenC)) tokens[i] = functionTokenC; - else if (lexeme == QLatin1String(pidTokenC)) + else if (lexeme == QLatin1StringView(pidTokenC)) tokens[i] = pidTokenC; - else if (lexeme == QLatin1String(appnameTokenC)) + else if (lexeme == QLatin1StringView(appnameTokenC)) tokens[i] = appnameTokenC; - else if (lexeme == QLatin1String(threadidTokenC)) + else if (lexeme == QLatin1StringView(threadidTokenC)) tokens[i] = threadidTokenC; - else if (lexeme == QLatin1String(qthreadptrTokenC)) + else if (lexeme == QLatin1StringView(qthreadptrTokenC)) tokens[i] = qthreadptrTokenC; - else if (lexeme.startsWith(QLatin1String(timeTokenC))) { + else if (lexeme.startsWith(QLatin1StringView(timeTokenC))) { tokens[i] = timeTokenC; - int spaceIdx = lexeme.indexOf(QChar::fromLatin1(' ')); + qsizetype spaceIdx = lexeme.indexOf(QChar::fromLatin1(' ')); if (spaceIdx > 0) - timeArgs.append(lexeme.mid(spaceIdx + 1, lexeme.length() - spaceIdx - 2)); + timeArgs.append(lexeme.mid(spaceIdx + 1, lexeme.size() - spaceIdx - 2)); else timeArgs.append(QString()); - } else if (lexeme.startsWith(QLatin1String(backtraceTokenC))) { + } else if (lexeme.startsWith(QLatin1StringView(backtraceTokenC))) { #ifdef QLOGGING_HAVE_BACKTRACE tokens[i] = backtraceTokenC; QString backtraceSeparator = QStringLiteral("|"); @@ -1209,7 +1265,7 @@ void QMessagePattern::setPattern(const QString &pattern) if (m.hasMatch()) { int depth = m.capturedView(1).toInt(); if (depth <= 0) - error += QLatin1String("QT_MESSAGE_PATTERN: %{backtrace} depth must be a number greater than 0\n"); + error += "QT_MESSAGE_PATTERN: %{backtrace} depth must be a number greater than 0\n"_L1; else backtraceDepth = depth; } @@ -1220,14 +1276,15 @@ void QMessagePattern::setPattern(const QString &pattern) backtraceParams.backtraceDepth = backtraceDepth; backtraceParams.backtraceSeparator = backtraceSeparator; backtraceArgs.append(backtraceParams); + maxBacktraceDepth = qMax(maxBacktraceDepth, backtraceDepth); #else - error += QLatin1String("QT_MESSAGE_PATTERN: %{backtrace} is not supported by this Qt build\n"); + error += "QT_MESSAGE_PATTERN: %{backtrace} is not supported by this Qt build\n"_L1; tokens[i] = ""; #endif } #define IF_TOKEN(LEVEL) \ - else if (lexeme == QLatin1String(LEVEL)) { \ + else if (lexeme == QLatin1StringView(LEVEL)) { \ if (inIf) \ nestedIfError = true; \ tokens[i] = LEVEL; \ @@ -1240,50 +1297,141 @@ void QMessagePattern::setPattern(const QString &pattern) IF_TOKEN(ifCriticalTokenC) IF_TOKEN(ifFatalTokenC) #undef IF_TOKEN - else if (lexeme == QLatin1String(endifTokenC)) { + else if (lexeme == QLatin1StringView(endifTokenC)) { tokens[i] = endifTokenC; if (!inIf && !nestedIfError) - error += QLatin1String("QT_MESSAGE_PATTERN: %{endif} without an %{if-*}\n"); + error += "QT_MESSAGE_PATTERN: %{endif} without an %{if-*}\n"_L1; inIf = false; } else { tokens[i] = emptyTokenC; - error += QStringLiteral("QT_MESSAGE_PATTERN: Unknown placeholder %1\n") - .arg(lexeme); + error += "QT_MESSAGE_PATTERN: Unknown placeholder "_L1 + lexeme + '\n'_L1; } } else { - char *literal = new char[lexeme.size() + 1]; - strncpy(literal, lexeme.toLatin1().constData(), lexeme.size()); - literal[lexeme.size()] = '\0'; - literalsVar.emplace_back(literal); - tokens[i] = literal; + using UP = std::unique_ptr<char[]>; + tokens[i] = literalsVar.emplace_back(UP(qstrdup(lexeme.toLatin1().constData()))).get(); } } if (nestedIfError) - error += QLatin1String("QT_MESSAGE_PATTERN: %{if-*} cannot be nested\n"); + error += "QT_MESSAGE_PATTERN: %{if-*} cannot be nested\n"_L1; else if (inIf) - error += QLatin1String("QT_MESSAGE_PATTERN: missing %{endif}\n"); + error += "QT_MESSAGE_PATTERN: missing %{endif}\n"_L1; - if (!error.isEmpty()) - qt_message_print(error); + if (!error.isEmpty()) { + // remove the last '\n' because the sinks deal with that on their own + error.chop(1); + + QMessageLogContext ctx(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, + "QMessagePattern::setPattern", nullptr); + preformattedMessageHandler(QtWarningMsg, ctx, error); + } literals.reset(new std::unique_ptr<const char[]>[literalsVar.size() + 1]); std::move(literalsVar.begin(), literalsVar.end(), &literals[0]); } -#if defined(QLOGGING_HAVE_BACKTRACE) && !defined(QT_BOOTSTRAPPED) +#if defined(QLOGGING_HAVE_BACKTRACE) // make sure the function has "Message" in the name so the function is removed +/* + A typical backtrace in debug mode looks like: + #0 QInternalMessageLogContext::populateBacktrace (this=0x7fffffffd660, frameCount=5) at qlogging.cpp:1342 + #1 QInternalMessageLogContext::QInternalMessageLogContext (logContext=..., this=<optimized out>) at qlogging_p.h:42 + #2 QDebug::~QDebug (this=0x7fffffffdac8, __in_chrg=<optimized out>) at qdebug.cpp:160 + + In release mode, the QInternalMessageLogContext constructor will be usually + inlined. Empirical testing with GCC 13 and Clang 17 suggest they do obey the + Q_ALWAYS_INLINE in that constructor even in debug mode and do inline it. + Unfortunately, we can't know for sure if it has been. +*/ +static constexpr int TypicalBacktraceFrameCount = 3; -#if ((defined(Q_CC_GNU) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS)) || __has_attribute(optimize)) \ - && !defined(Q_CC_INTEL) && !defined(Q_CC_CLANG) -// force skipping the frame pointer, to save the backtrace() function some work -__attribute__((optimize("omit-frame-pointer"))) -#endif -static QStringList backtraceFramesForLogMessage(int frameCount) +Q_NEVER_INLINE void QInternalMessageLogContext::populateBacktrace(int frameCount) +{ + assert(frameCount >= 0); + BacktraceStorage &result = backtrace.emplace(TypicalBacktraceFrameCount + frameCount); + int n = ::backtrace(result.data(), result.size()); + if (n <= 0) + result.clear(); + else + result.resize(n); +} + +static QStringList +backtraceFramesForLogMessage(int frameCount, + const QInternalMessageLogContext::BacktraceStorage &buffer) { + struct DecodedFrame { + QString library; + QString function; + }; + QStringList result; if (frameCount == 0) return result; + auto shouldSkipFrame = [&result](const auto &library, const auto &function) { + if (!result.isEmpty() || !library.contains("Qt6Core"_L1)) + return false; + if (function.isEmpty()) + return true; + if (function.contains("6QDebug"_L1)) + return true; + if (function.contains("14QMessageLogger"_L1)) + return true; + if (function.contains("17qt_message_output"_L1)) + return true; + if (function.contains("26QInternalMessageLogContext"_L1)) + return true; + return false; + }; + + auto demangled = [](auto &function) -> QString { + if (!function.startsWith("_Z"_L1)) + return function; + + // we optimize for the case where __cxa_demangle succeeds + auto fn = [&]() { + if constexpr (sizeof(function.at(0)) == 1) + return function.data(); // -> const char * + else + return std::move(function).toUtf8(); // -> QByteArray + }(); + QScopedPointer<char, QScopedPointerPodDeleter> demangled; + demangled.reset(abi::__cxa_demangle(fn, nullptr, nullptr, nullptr)); + + if (demangled) + return QString::fromUtf8(qCleanupFuncinfo(demangled.data())); + else + return QString::fromUtf8(fn); // restore + }; + +# if QT_CONFIG(dladdr) + // use dladdr() instead of backtrace_symbols() + QString cachedLibrary; + const char *cachedFname = nullptr; + auto decodeFrame = [&](void *addr) -> DecodedFrame { + Dl_info info; + if (!dladdr(addr, &info)) + return {}; + + // These are actually UTF-8, so we'll correct below + QLatin1StringView fn(info.dli_sname); + QLatin1StringView lib; + if (const char *lastSlash = strrchr(info.dli_fname, '/')) + lib = QLatin1StringView(lastSlash + 1); + else + lib = QLatin1StringView(info.dli_fname); + + if (shouldSkipFrame(lib, fn)) + return {}; + + QString function = demangled(fn); + if (lib.data() != cachedFname) { + cachedFname = lib.data(); + cachedLibrary = QString::fromUtf8(cachedFname, lib.size()); + } + return { cachedLibrary, function }; + }; +# else // The results of backtrace_symbols looks like this: // /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413] // The offset and function name are optional. @@ -1291,63 +1439,65 @@ static QStringList backtraceFramesForLogMessage(int frameCount) // This code is protected by QMessagePattern::mutex so it is thread safe on all compilers static const QRegularExpression rx(QStringLiteral("^(?:[^(]*/)?([^(/]+)\\(([^+]*)(?:[\\+[a-f0-9x]*)?\\) \\[[a-f0-9x]*\\]$")); - QVarLengthArray<void *, 32> buffer(8 + frameCount); - int n = backtrace(buffer.data(), buffer.size()); - if (n > 0) { - int numberPrinted = 0; - for (int i = 0; i < n && numberPrinted < frameCount; ++i) { - QScopedPointer<char*, QScopedPointerPodDeleter> strings(backtrace_symbols(buffer.data() + i, 1)); - QString trace = QString::fromLatin1(strings.data()[0]); - QRegularExpressionMatch m = rx.match(trace); - if (m.hasMatch()) { - QString library = m.captured(1); - QString function = m.captured(2); - - // skip the trace from QtCore that are because of the qDebug itself - if (!numberPrinted && library.contains(QLatin1String("Qt6Core")) - && (function.isEmpty() || function.contains(QLatin1String("Message"), Qt::CaseInsensitive) - || function.contains(QLatin1String("QDebug")))) { - continue; - } + auto decodeFrame = [&](void *&addr) -> DecodedFrame { + QScopedPointer<char*, QScopedPointerPodDeleter> strings(backtrace_symbols(&addr, 1)); + QString trace = QString::fromUtf8(strings.data()[0]); + QRegularExpressionMatch m = rx.match(trace); + if (!m.hasMatch()) + return {}; - if (function.startsWith(QLatin1String("_Z"))) { - QScopedPointer<char, QScopedPointerPodDeleter> demangled( - abi::__cxa_demangle(function.toUtf8(), nullptr, nullptr, nullptr)); - if (demangled) - function = QString::fromUtf8(qCleanupFuncinfo(demangled.data())); - } + QString library = m.captured(1); + QString function = m.captured(2); - if (function.isEmpty()) { - result.append(QLatin1Char('?') + library + QLatin1Char('?')); - } else { - result.append(function); - } - } else { - if (numberPrinted == 0) { - // innermost, unknown frames are usually the logging framework itself - continue; - } + // skip the trace from QtCore that are because of the qDebug itself + if (shouldSkipFrame(library, function)) + return {}; + + function = demangled(function); + return { library, function }; + }; +# endif + + for (void *const &addr : buffer) { + DecodedFrame frame = decodeFrame(addr); + if (!frame.library.isEmpty()) { + if (frame.function.isEmpty()) + result.append(u'?' + frame.library + u'?'); + else + result.append(frame.function); + } else { + // innermost, unknown frames are usually the logging framework itself + if (!result.isEmpty()) result.append(QStringLiteral("???")); - } - numberPrinted++; } + + if (result.size() == frameCount) + break; } return result; } static QString formatBacktraceForLogMessage(const QMessagePattern::BacktraceParams backtraceParams, - const char *function) + const QMessageLogContext &ctx) { + // do we have a backtrace stored? + if (ctx.version <= QMessageLogContext::CurrentVersion) + return QString(); + + auto &fullctx = static_cast<const QInternalMessageLogContext &>(ctx); + if (!fullctx.backtrace.has_value()) + return QString(); + QString backtraceSeparator = backtraceParams.backtraceSeparator; int backtraceDepth = backtraceParams.backtraceDepth; - QStringList frames = backtraceFramesForLogMessage(backtraceDepth); + QStringList frames = backtraceFramesForLogMessage(backtraceDepth, *fullctx.backtrace); if (frames.isEmpty()) return QString(); // if the first frame is unknown, replace it with the context function - if (function && frames.at(0).startsWith(QLatin1Char('?'))) - frames[0] = QString::fromUtf8(qCleanupFuncinfo(function)); + if (ctx.function && frames.at(0).startsWith(u'?')) + frames[0] = QString::fromUtf8(qCleanupFuncinfo(ctx.function)); return frames.join(backtraceSeparator); } @@ -1356,7 +1506,7 @@ static QString formatBacktraceForLogMessage(const QMessagePattern::BacktracePara Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern) /*! - \relates <QtGlobal> + \relates <QtLogging> \since 5.4 Generates a formatted string out of the \a type, \a context, \a str arguments. @@ -1371,6 +1521,14 @@ Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern) */ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str) { + return formatLogMessage(type, context, str); +} + +// Separate function so the default message handler can bypass the public, +// exported function above. Static functions can't get added to the dynamic +// symbol tables, so they never show up in backtrace_symbols() or equivalent. +static QString formatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str) +{ QString message; const auto locker = qt_scoped_lock(QMessagePattern::mutex); @@ -1384,12 +1542,10 @@ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, con bool skip = false; -#ifndef QT_BOOTSTRAPPED int timeArgsIdx = 0; #ifdef QLOGGING_HAVE_BACKTRACE int backtraceArgsIdx = 0; #endif -#endif // we do not convert file, function, line literals to local encoding due to overhead for (int i = 0; pattern->tokens[i]; ++i) { @@ -1399,42 +1555,39 @@ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, con } else if (skip) { // we skip adding messages, but we have to iterate over // timeArgsIdx and backtraceArgsIdx anyway -#ifndef QT_BOOTSTRAPPED if (token == timeTokenC) timeArgsIdx++; #ifdef QLOGGING_HAVE_BACKTRACE else if (token == backtraceTokenC) backtraceArgsIdx++; #endif -#endif } else if (token == messageTokenC) { message.append(str); } else if (token == categoryTokenC) { #ifndef Q_OS_ANDROID // Don't add the category to the message on Android - message.append(QLatin1String(context.category)); + message.append(QLatin1StringView(context.category)); #endif } else if (token == typeTokenC) { switch (type) { - case QtDebugMsg: message.append(QLatin1String("debug")); break; - case QtInfoMsg: message.append(QLatin1String("info")); break; - case QtWarningMsg: message.append(QLatin1String("warning")); break; - case QtCriticalMsg:message.append(QLatin1String("critical")); break; - case QtFatalMsg: message.append(QLatin1String("fatal")); break; + case QtDebugMsg: message.append("debug"_L1); break; + case QtInfoMsg: message.append("info"_L1); break; + case QtWarningMsg: message.append("warning"_L1); break; + case QtCriticalMsg:message.append("critical"_L1); break; + case QtFatalMsg: message.append("fatal"_L1); break; } } else if (token == fileTokenC) { if (context.file) - message.append(QLatin1String(context.file)); + message.append(QLatin1StringView(context.file)); else - message.append(QLatin1String("unknown")); + message.append("unknown"_L1); } else if (token == lineTokenC) { message.append(QString::number(context.line)); } else if (token == functionTokenC) { if (context.function) message.append(QString::fromLatin1(qCleanupFuncinfo(context.function))); else - message.append(QLatin1String("unknown")); -#ifndef QT_BOOTSTRAPPED + message.append("unknown"_L1); } else if (token == pidTokenC) { message.append(QString::number(QCoreApplication::applicationPid())); } else if (token == appnameTokenC) { @@ -1443,24 +1596,24 @@ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, con // print the TID as decimal message.append(QString::number(qt_gettid())); } else if (token == qthreadptrTokenC) { - message.append(QLatin1String("0x")); + message.append("0x"_L1); message.append(QString::number(qlonglong(QThread::currentThread()->currentThread()), 16)); #ifdef QLOGGING_HAVE_BACKTRACE } else if (token == backtraceTokenC) { QMessagePattern::BacktraceParams backtraceParams = pattern->backtraceArgs.at(backtraceArgsIdx); backtraceArgsIdx++; - message.append(formatBacktraceForLogMessage(backtraceParams, context.function)); + message.append(formatBacktraceForLogMessage(backtraceParams, context)); #endif } else if (token == timeTokenC) { QString timeFormat = pattern->timeArgs.at(timeArgsIdx); timeArgsIdx++; - if (timeFormat == QLatin1String("process")) { - quint64 ms = pattern->timer.elapsed(); - message.append(QString::asprintf("%6d.%03d", uint(ms / 1000), uint(ms % 1000))); - } else if (timeFormat == QLatin1String("boot")) { + if (timeFormat == "process"_L1) { + quint64 ms = pattern->timer.elapsed(); + message.append(QString::asprintf("%6d.%03d", uint(ms / 1000), uint(ms % 1000))); + } else if (timeFormat == "boot"_L1) { // just print the milliseconds since the elapsed timer reference // like the Linux kernel does - uint ms = QDeadlineTimer::current().deadline(); + qint64 ms = QDeadlineTimer::current().deadline(); message.append(QString::asprintf("%6d.%03d", uint(ms / 1000), uint(ms % 1000))); #if QT_CONFIG(datestring) } else if (timeFormat.isEmpty()) { @@ -1469,7 +1622,6 @@ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, con message.append(QDateTime::currentDateTime().toString(timeFormat)); #endif // QT_CONFIG(datestring) } -#endif // !QT_BOOTSTRAPPED } else if (token == ifCategoryTokenC) { if (isDefaultCategory(context.category)) skip = true; @@ -1483,16 +1635,30 @@ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, con HANDLE_IF_TOKEN(Fatal) #undef HANDLE_IF_TOKEN } else { - message.append(QLatin1String(token)); + message.append(QLatin1StringView(token)); } } return message; } +#else // QT_BOOTSTRAPPED +static QString formatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str) +{ + Q_UNUSED(type); + Q_UNUSED(context); + return str; +} +#endif +#ifndef QLOGGING_HAVE_BACKTRACE +void QInternalMessageLogContext::populateBacktrace(int) +{ + Q_UNREACHABLE(); +} +#endif static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &buf); // pointer to QtMessageHandler debug handler (with context) -static QBasicAtomicPointer<void (QtMsgType, const QMessageLogContext &, const QString &)> messageHandler = Q_BASIC_ATOMIC_INITIALIZER(nullptr); +Q_CONSTINIT static QBasicAtomicPointer<void (QtMsgType, const QMessageLogContext &, const QString &)> messageHandler = Q_BASIC_ATOMIC_INITIALIZER(nullptr); // ------------------------ Alternate logging sinks ------------------------- @@ -1505,13 +1671,14 @@ static QBasicAtomicPointer<void (QtMsgType, const QMessageLogContext &, const QS #define QT_LOG_CODE 9000 #endif -static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) +static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &, + const QString &message) { if (shouldLogToStderr()) return false; // Leave logging up to stderr handler - QString formattedMessage = qFormatLogMessage(type, context, message); - formattedMessage.append(QLatin1Char('\n')); + QString formattedMessage = message; + formattedMessage.append(u'\n'); if (slog2_set_default_buffer((slog2_buffer_t)-1) == 0) { slog2_buffer_set_config_t buffer_config; slog2_buffer_t buffer_handle; @@ -1532,7 +1699,7 @@ static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &cont // Set as the default buffer slog2_set_default_buffer(buffer_handle); } - int severity; + int severity = SLOG2_INFO; //Determines the severity level switch (type) { case QtDebugMsg: @@ -1561,13 +1728,11 @@ static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &cont #if QT_CONFIG(journald) static bool systemd_default_message_handler(QtMsgType type, const QMessageLogContext &context, - const QString &message) + const QString &formattedMessage) { if (shouldLogToStderr()) return false; // Leave logging up to stderr handler - QString formattedMessage = qFormatLogMessage(type, context, message); - int priority = LOG_INFO; // Informational switch (type) { case QtDebugMsg: @@ -1600,13 +1765,12 @@ static bool systemd_default_message_handler(QtMsgType type, #endif #if QT_CONFIG(syslog) -static bool syslog_default_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) +static bool syslog_default_message_handler(QtMsgType type, const QMessageLogContext &context, + const QString &formattedMessage) { if (shouldLogToStderr()) return false; // Leave logging up to stderr handler - QString formattedMessage = qFormatLogMessage(type, context, message); - int priority = LOG_INFO; // Informational switch (type) { case QtDebugMsg: @@ -1635,13 +1799,11 @@ static bool syslog_default_message_handler(QtMsgType type, const QMessageLogCont #ifdef Q_OS_ANDROID static bool android_default_message_handler(QtMsgType type, const QMessageLogContext &context, - const QString &message) + const QString &formattedMessage) { if (shouldLogToStderr()) return false; // Leave logging up to stderr handler - QString formattedMessage = qFormatLogMessage(type, context, message); - android_LogPriority priority = ANDROID_LOG_DEBUG; switch (type) { case QtDebugMsg: @@ -1661,9 +1823,10 @@ static bool android_default_message_handler(QtMsgType type, break; }; + // If application name is a tag ensure it has no spaces // If a category is defined, use it as an Android logging tag __android_log_print(priority, isDefaultCategory(context.category) ? - qPrintable(QCoreApplication::applicationName()) : context.category, + qPrintable(QCoreApplication::applicationName().replace(u' ', u'_')) : context.category, "%s\n", qPrintable(formattedMessage)); return true; // Prevent further output to stderr @@ -1671,10 +1834,10 @@ static bool android_default_message_handler(QtMsgType type, #endif //Q_OS_ANDROID #ifdef Q_OS_WIN -static void win_outputDebugString_helper(QStringView message) +static void win_outputDebugString_helper(const QString &message) { const qsizetype maxOutputStringLength = 32766; - static QBasicMutex m; + Q_CONSTINIT static QBasicMutex m; auto locker = qt_unique_lock(m); // fast path: Avoid string copies if one output is enough if (message.length() <= maxOutputStringLength) { @@ -1683,7 +1846,7 @@ static void win_outputDebugString_helper(QStringView message) wchar_t *messagePart = new wchar_t[maxOutputStringLength + 1]; for (qsizetype i = 0; i < message.length(); i += maxOutputStringLength) { const qsizetype length = qMin(message.length() - i, maxOutputStringLength); - const qsizetype len = message.mid(i, length).toWCharArray(messagePart); + const qsizetype len = QStringView{message}.mid(i, length).toWCharArray(messagePart); Q_ASSERT(len == length); messagePart[len] = 0; OutputDebugString(messagePart); @@ -1692,13 +1855,13 @@ static void win_outputDebugString_helper(QStringView message) } } -static bool win_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) +static bool win_message_handler(QtMsgType, const QMessageLogContext &, + const QString &formattedMessage) { if (shouldLogToStderr()) return false; // Leave logging up to stderr handler - const QString formattedMessage = qFormatLogMessage(type, context, message).append(QLatin1Char('\n')); - win_outputDebugString_helper(formattedMessage); + win_outputDebugString_helper(formattedMessage + u'\n'); return true; // Prevent further output to stderr } @@ -1706,15 +1869,15 @@ static bool win_message_handler(QtMsgType type, const QMessageLogContext &contex #ifdef Q_OS_WASM static bool wasm_default_message_handler(QtMsgType type, - const QMessageLogContext &context, - const QString &message) + const QMessageLogContext &, + const QString &formattedMessage) { - if (shouldLogToStderr()) - return false; // Leave logging up to stderr handler + static bool forceStderrLogging = qEnvironmentVariableIntValue("QT_FORCE_STDERR_LOGGING"); + if (forceStderrLogging) + return false; - QString formattedMessage = qFormatLogMessage(type, context, message); - int emOutputFlags = (EM_LOG_CONSOLE | EM_LOG_DEMANGLE); - QByteArray localMsg = message.toLocal8Bit(); + int emOutputFlags = EM_LOG_CONSOLE; + QByteArray localMsg = formattedMessage.toLocal8Bit(); switch (type) { case QtDebugMsg: break; @@ -1739,58 +1902,89 @@ static bool wasm_default_message_handler(QtMsgType type, // -------------------------------------------------------------------------- -static void stderr_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) +static void stderr_message_handler(QtMsgType type, const QMessageLogContext &context, + const QString &formattedMessage) { - QString formattedMessage = qFormatLogMessage(type, context, message); + Q_UNUSED(type); + Q_UNUSED(context); // print nothing if message pattern didn't apply / was empty. // (still print empty lines, e.g. because message itself was empty) if (formattedMessage.isNull()) return; - fprintf(stderr, "%s\n", formattedMessage.toLocal8Bit().constData()); fflush(stderr); } +namespace { +struct SystemMessageSink +{ + using Fn = bool(QtMsgType, const QMessageLogContext &, const QString &); + Fn *sink; + bool messageIsUnformatted = false; +}; +} + +static constexpr SystemMessageSink systemMessageSink = { +#if defined(QT_BOOTSTRAPPED) + nullptr +#elif defined(Q_OS_WIN) + win_message_handler +#elif QT_CONFIG(slog2) + slog2_default_handler +#elif QT_CONFIG(journald) + systemd_default_message_handler +#elif QT_CONFIG(syslog) + syslog_default_message_handler +#elif defined(Q_OS_ANDROID) + android_default_message_handler +#elif defined(QT_USE_APPLE_UNIFIED_LOGGING) + AppleUnifiedLogger::messageHandler, true +#elif defined Q_OS_WASM + wasm_default_message_handler +#else + nullptr +#endif +}; + +static void preformattedMessageHandler(QtMsgType type, const QMessageLogContext &context, + const QString &formattedMessage) +{ +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Waddress") // "the address of ~~ will never be NULL + if (systemMessageSink.sink && systemMessageSink.sink(type, context, formattedMessage)) + return; +QT_WARNING_POP + + stderr_message_handler(type, context, formattedMessage); +} + /*! \internal */ 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) - 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 (systemMessageSink.messageIsUnformatted) { + if (systemMessageSink.sink(type, context, message)) + return; + } - if (!handledStderr) - stderr_message_handler(type, context, message); + preformattedMessageHandler(type, context, formatLogMessage(type, context, message)); } -#if defined(Q_COMPILER_THREAD_LOCAL) +#if defined(QT_BOOTSTRAPPED) +// there's no message handler in bootstrapped mode; force everything to stderr +static bool grabMessageHandler() { return false; } +static void ungrabMessageHandler() { } + +#elif defined(Q_COMPILER_THREAD_LOCAL) -static thread_local bool msgHandlerGrabbed = false; +Q_CONSTINIT static thread_local bool msgHandlerGrabbed = false; static bool grabMessageHandler() { @@ -1832,25 +2026,14 @@ static void qt_message_print(QtMsgType msgType, const QMessageLogContext &contex auto msgHandler = messageHandler.loadAcquire(); (msgHandler ? msgHandler : qDefaultMessageHandler)(msgType, context, message); } else { - fprintf(stderr, "%s\n", message.toLocal8Bit().constData()); - } -} - -static void qt_message_print(const QString &message) -{ -#if defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED) - if (!shouldLogToStderr()) { - win_outputDebugString_helper(message); - return; + stderr_message_handler(msgType, context, message); } -#endif - fprintf(stderr, "%s", message.toLocal8Bit().constData()); - fflush(stderr); } -static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, const QString &message) +template <typename String> +static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, String &&message) { -#if defined(Q_CC_MSVC) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR) +#if defined(Q_CC_MSVC_ONLY) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR) wchar_t contextFileL[256]; // we probably should let the compiler do this for us, by declaring QMessageLogContext::file to // be const wchar_t * in the first place, but the #ifdefery above is very complex and we @@ -1869,21 +2052,24 @@ static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, const _CrtDbgBreak(); #else Q_UNUSED(context); - Q_UNUSED(message); #endif + if constexpr (std::is_class_v<String> && !std::is_const_v<String>) + message.clear(); + else + Q_UNUSED(message); qAbort(); } - /*! \internal */ void qt_message_output(QtMsgType msgType, const QMessageLogContext &context, const QString &message) { - qt_message_print(msgType, context, message); + QInternalMessageLogContext ctx(context); + qt_message_print(msgType, ctx, message); if (isFatal(msgType)) - qt_message_fatal(msgType, context, message); + qt_message_fatal(msgType, ctx, message); } void qErrnoWarning(const char *msg, ...) @@ -1897,9 +2083,9 @@ void qErrnoWarning(const char *msg, ...) QString buf = QString::vasprintf(msg, ap); va_end(ap); - buf += QLatin1String(" (") + error_string + QLatin1Char(')'); - QMessageLogContext context; - qt_message_output(QtCriticalMsg, context, buf); + buf += " ("_L1 + error_string + u')'; + QInternalMessageLogContext context{QMessageLogContext()}; + qt_message_output(QtWarningMsg, context, buf); } void qErrnoWarning(int code, const char *msg, ...) @@ -1911,14 +2097,14 @@ void qErrnoWarning(int code, const char *msg, ...) QString buf = QString::vasprintf(msg, ap); va_end(ap); - buf += QLatin1String(" (") + qt_error_string(code) + QLatin1Char(')'); - QMessageLogContext context; - qt_message_output(QtCriticalMsg, context, buf); + buf += " ("_L1 + qt_error_string(code) + u')'; + QInternalMessageLogContext context{QMessageLogContext()}; + qt_message_output(QtWarningMsg, context, buf); } /*! \typedef QtMessageHandler - \relates <QtGlobal> + \relates <QtLogging> \since 5.0 This is a typedef for a pointer to a function with the following @@ -1931,41 +2117,69 @@ void qErrnoWarning(int code, const char *msg, ...) /*! \fn QtMessageHandler qInstallMessageHandler(QtMessageHandler handler) - \relates <QtGlobal> + \relates <QtLogging> \since 5.0 - Installs a Qt message \a handler which has been defined - previously. Returns a pointer to the previous message handler. - - The message handler is a function that prints out debug messages, - warnings, critical and fatal error messages. The Qt library (debug - mode) contains hundreds of warning messages that are printed - when internal errors (usually invalid function arguments) - occur. Qt built in release mode also contains such warnings unless - QT_NO_WARNING_OUTPUT and/or QT_NO_DEBUG_OUTPUT have been set during - compilation. If you implement your own message handler, you get total - control of these messages. - - The default message handler prints the message to the standard - output under X11 or to the debugger under Windows. If it is a - fatal message, the application aborts immediately. - - Only one message handler can be defined, since this is usually - done on an application-wide basis to control debug output. - - To restore the message handler, call \c qInstallMessageHandler(0). - - Example: + Installs a Qt message \a handler. + Returns a pointer to the previously installed message handler. + + A message handler is a function that prints out debug, info, + warning, critical, and fatal messages from Qt's logging infrastructure. + By default, Qt uses a standard message handler that formats and + prints messages to different sinks specific to the operating system + and Qt configuration. Installing your own message handler allows you + to assume full control, and for instance log messages to the + file system. + + Note that Qt supports \l{QLoggingCategory}{logging categories} for + grouping related messages in semantic categories. You can use these + to enable or disable logging per category and \l{QtMsgType}{message type}. + As the filtering for logging categories is done even before a message + is created, messages for disabled types and categories will not reach + the message handler. + + A message handler needs to be + \l{Reentrancy and Thread-Safety}{reentrant}. That is, it might be called + from different threads, in parallel. Therefore, writes to common sinks + (like a database, or a file) often need to be synchronized. + + Qt allows to enrich logging messages with further meta-information + by calling \l qSetMessagePattern(), or setting the \c QT_MESSAGE_PATTERN + environment variable. To keep this formatting, a custom message handler + can use \l qFormatLogMessage(). + + Try to keep the code in the message handler itself minimal, as expensive + operations might block the application. Also, to avoid recursion, any + logging messages generated in the message handler itself will be ignored. + + The message handler should always return. For + \l{QtFatalMsg}{fatal messages}, the application aborts immediately after + handling that message. + + Only one message handler can be installed at a time, for the whole application. + If there was a previous custom message handler installed, + the function will return a pointer to it. This handler can then + be later reinstalled by another call to the method. Also, calling + \c qInstallMessageHandler(nullptr) will restore the default + message handler. + + Here is an example of a message handler that logs to a local file + before calling the default handler: \snippet code/src_corelib_global_qglobal.cpp 23 + Note that the C++ standard guarantees that \c{static FILE *f} is + initialized in a thread-safe way. We can also expect \c{fprintf()} + and \c{fflush()} to be thread-safe, so no further synchronization + is necessary. + \sa QtMessageHandler, QtMsgType, qDebug(), qInfo(), qWarning(), qCritical(), qFatal(), - {Debugging Techniques} + {Debugging Techniques}, qFormatLogMessage() */ /*! \fn void qSetMessagePattern(const QString &pattern) - \relates <QtGlobal> + \relates <QtLogging> \since 5.0 \brief Changes the output of the default message handler. @@ -1998,9 +2212,12 @@ void qErrnoWarning(int code, const char *msg, ...) \row \li \c{%{backtrace [depth=N] [separator="..."]}} \li A backtrace with the number of frames specified by the optional \c depth parameter (defaults to 5), and separated by the optional \c 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 \c{QMAKE_LFLAGS += -rdynamic}. + in your application, make sure your application is compiled and linked with \c{-rdynamic}, + or an equivalent of it. + When reading backtraces, take into account that frames might be missing due to inlining or tail call optimization. \endtable @@ -2015,7 +2232,7 @@ void qErrnoWarning(int code, const char *msg, ...) Example: \snippet code/src_corelib_global_qlogging.cpp 0 - The default \a pattern is "%{if-category}%{category}: %{endif}%{message}". + The default \a pattern is \c{%{if-category}%{category}: %{endif}%{message}}. The \a pattern can also be changed at runtime by setting the QT_MESSAGE_PATTERN environment variable; if both \l qSetMessagePattern() is called and QT_MESSAGE_PATTERN is @@ -2043,6 +2260,7 @@ QtMessageHandler qInstallMessageHandler(QtMessageHandler h) return qDefaultMessageHandler; } +#ifndef QT_BOOTSTRAPPED void qSetMessagePattern(const QString &pattern) { const auto locker = qt_scoped_lock(QMessagePattern::mutex); @@ -2050,7 +2268,38 @@ void qSetMessagePattern(const QString &pattern) if (!qMessagePattern()->fromEnvironment) qMessagePattern()->setPattern(pattern); } +#endif +static void copyInternalContext(QInternalMessageLogContext *self, + const QMessageLogContext &logContext) noexcept +{ + if (logContext.version == self->version) { + auto other = static_cast<const QInternalMessageLogContext *>(&logContext); + self->backtrace = other->backtrace; + } +} + +/*! + \internal + Copies context information from \a logContext into this QMessageLogContext. + Returns the number of backtrace frames that are desired. +*/ +int QInternalMessageLogContext::initFrom(const QMessageLogContext &logContext) +{ + version = CurrentVersion + 1; + copyContextFrom(logContext); + +#ifdef QLOGGING_HAVE_BACKTRACE + if (backtrace.has_value()) + return 0; // we have a stored backtrace, no need to get it again + + // initializes the message pattern, if needed + if (auto pattern = qMessagePattern()) + return pattern->maxBacktraceDepth; +#endif + + return 0; +} /*! Copies context information from \a logContext into this QMessageLogContext. @@ -2066,6 +2315,8 @@ QMessageLogContext &QMessageLogContext::copyContextFrom(const QMessageLogContext this->file = logContext.file; this->line = logContext.line; this->function = logContext.function; + if (Q_UNLIKELY(version == CurrentVersion + 1)) + copyInternalContext(static_cast<QInternalMessageLogContext *>(this), logContext); return *this; } @@ -2113,4 +2364,219 @@ QMessageLogContext &QMessageLogContext::copyContextFrom(const QMessageLogContext \a lineNumber, in function \a functionName, and category \a categoryName. */ +/*! + \macro qDebug(const char *message, ...) + \relates <QtLogging> + \threadsafe + + Calls the message handler with the debug message \a message. If no + message handler has been installed, the message is printed to + stderr. Under Windows the message is sent to the console, if it is a + console application; otherwise, it is sent to the debugger. On QNX, the + message is sent to slogger2. This function does nothing if \c QT_NO_DEBUG_OUTPUT + was defined during compilation. + + If you pass the function a format string and a list of arguments, + it works in similar way to the C printf() function. The format + should be a Latin-1 string. + + Example: + + \snippet code/src_corelib_global_qglobal.cpp 24 + + If you include \c <QtDebug>, a more convenient syntax is also + available: + + \snippet code/src_corelib_global_qglobal.cpp 25 + + With this syntax, the function returns a QDebug object that is + configured to use the QtDebugMsg message type. It automatically + puts a single space between each item, and outputs a newline at + the end. It supports many C++ and Qt types. + + To suppress the output at runtime, install your own message handler + with qInstallMessageHandler(). + + \sa qInfo(), qWarning(), qCritical(), qFatal(), qInstallMessageHandler(), + {Debugging Techniques} +*/ + +/*! + \macro qInfo(const char *message, ...) + \relates <QtLogging> + \threadsafe + \since 5.5 + + Calls the message handler with the informational message \a message. If no + message handler has been installed, the message is printed to + stderr. Under Windows, the message is sent to the console, if it is a + console application; otherwise, it is sent to the debugger. On QNX the + message is sent to slogger2. This function does nothing if \c QT_NO_INFO_OUTPUT + was defined during compilation. + + If you pass the function a format string and a list of arguments, + it works in similar way to the C printf() function. The format + should be a Latin-1 string. + + Example: + + \snippet code/src_corelib_global_qglobal.cpp qInfo_printf + + If you include \c <QtDebug>, a more convenient syntax is also + available: + + \snippet code/src_corelib_global_qglobal.cpp qInfo_stream + + With this syntax, the function returns a QDebug object that is + configured to use the QtInfoMsg message type. It automatically + puts a single space between each item, and outputs a newline at + the end. It supports many C++ and Qt types. + + To suppress the output at runtime, install your own message handler + using qInstallMessageHandler(). + + \sa qDebug(), qWarning(), qCritical(), qFatal(), qInstallMessageHandler(), + {Debugging Techniques} +*/ + +/*! + \macro qWarning(const char *message, ...) + \relates <QtLogging> + \threadsafe + + Calls the message handler with the warning message \a message. If no + message handler has been installed, the message is printed to + stderr. Under Windows, the message is sent to the debugger. + On QNX the message is sent to slogger2. + + This function takes a format string and a list of arguments, + similar to the C printf() function. The format should be a Latin-1 + string. + + Example: + \snippet code/src_corelib_global_qglobal.cpp 26 + + If you include <QtDebug>, a more convenient syntax is + also available: + + \snippet code/src_corelib_global_qglobal.cpp 27 + + This syntax inserts a space between each item, and + appends a newline at the end. + + This function does nothing if \c QT_NO_WARNING_OUTPUT was defined + during compilation. + To suppress the output at runtime, you can set + \l{QLoggingCategory}{logging rules} or register a custom + \l{QLoggingCategory::installFilter()}{filter}. + + For debugging purposes, it is sometimes convenient to let the + program abort for warning messages. This allows you then + to inspect the core dump, or attach a debugger - see also \l{qFatal()}. + To enable this, set the environment variable \c{QT_FATAL_WARNINGS} + to a number \c n. The program terminates then for the n-th warning. + That is, if the environment variable is set to 1, it will terminate + on the first call; if it contains the value 10, it will exit on the 10th + call. Any non-numeric value in the environment variable is equivalent to 1. + + \sa qDebug(), qInfo(), qCritical(), qFatal(), qInstallMessageHandler(), + {Debugging Techniques} +*/ + +/*! + \macro qCritical(const char *message, ...) + \relates <QtLogging> + \threadsafe + + Calls the message handler with the critical message \a message. If no + message handler has been installed, the message is printed to + stderr. Under Windows, the message is sent to the debugger. + On QNX the message is sent to slogger2. + + This function takes a format string and a list of arguments, + similar to the C printf() function. The format should be a Latin-1 + string. + + Example: + \snippet code/src_corelib_global_qglobal.cpp 28 + + If you include <QtDebug>, a more convenient syntax is + also available: + + \snippet code/src_corelib_global_qglobal.cpp 29 + + A space is inserted between the items, and a newline is + appended at the end. + + To suppress the output at runtime, you can define + \l{QLoggingCategory}{logging rules} or register a custom + \l{QLoggingCategory::installFilter()}{filter}. + + For debugging purposes, it is sometimes convenient to let the + program abort for critical messages. This allows you then + to inspect the core dump, or attach a debugger - see also \l{qFatal()}. + To enable this, set the environment variable \c{QT_FATAL_CRITICALS} + to a number \c n. The program terminates then for the n-th critical + message. + That is, if the environment variable is set to 1, it will terminate + on the first call; if it contains the value 10, it will exit on the 10th + call. Any non-numeric value in the environment variable is equivalent to 1. + + \sa qDebug(), qInfo(), qWarning(), qFatal(), qInstallMessageHandler(), + {Debugging Techniques} +*/ + +/*! + \macro qFatal(const char *message, ...) + \relates <QtLogging> + + Calls the message handler with the fatal message \a message. If no + message handler has been installed, the message is printed to + stderr. Under Windows, the message is sent to the debugger. + On QNX the message is sent to slogger2. + + If you are using the \b{default message handler} this function will + abort to create a core dump. On Windows, for debug builds, + this function will report a _CRT_ERROR enabling you to connect a debugger + to the application. + + This function takes a format string and a list of arguments, + similar to the C printf() function. + + Example: + \snippet code/src_corelib_global_qglobal.cpp 30 + + To suppress the output at runtime, install your own message handler + with qInstallMessageHandler(). + + \sa qDebug(), qInfo(), qWarning(), qCritical(), qInstallMessageHandler(), + {Debugging Techniques} +*/ + +/*! + \enum QtMsgType + \relates <QtLogging> + + This enum describes the messages that can be sent to a message + handler (QtMessageHandler). You can use the enum to identify and + associate the various message types with the appropriate + actions. + + \value QtDebugMsg + A message generated by the qDebug() function. + \value QtInfoMsg + A message generated by the qInfo() function. + \value QtWarningMsg + A message generated by the qWarning() function. + \value QtCriticalMsg + A message generated by the qCritical() function. + \value QtFatalMsg + A message generated by the qFatal() function. + \omitvalue QtSystemMsg + + \c QtInfoMsg was added in Qt 5.5. + + \sa QtMessageHandler, qInstallMessageHandler() +*/ + QT_END_NAMESPACE |