summaryrefslogtreecommitdiffstats
path: root/src/corelib/global/qlogging.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/global/qlogging.cpp')
-rw-r--r--src/corelib/global/qlogging.cpp1238
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