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.cpp1273
1 files changed, 859 insertions, 414 deletions
diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp
index f4fac3f35e..301a52cd57 100644
--- a/src/corelib/global/qlogging.cpp
+++ b/src/corelib/global/qlogging.cpp
@@ -1,43 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 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.
@@ -348,7 +344,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 +365,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 +382,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 +398,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 +413,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 +431,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 +453,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 +532,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 +554,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 +610,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 +618,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 +637,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 +659,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 +712,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 +720,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 +739,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 +761,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 +815,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 +868,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 +942,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 +969,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 +1001,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 +1024,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 +1080,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) {
@@ -1090,7 +1142,8 @@ struct QMessagePattern
QString backtraceSeparator;
int backtraceDepth;
};
- QList<BacktraceParams> backtraceArgs; // backtrace argumens in sequence of %{backtrace
+ QList<BacktraceParams> backtraceArgs; // backtrace arguments in sequence of %{backtrace
+ int maxBacktraceDepth = 0;
#endif
bool fromEnvironment;
@@ -1100,7 +1153,7 @@ struct QMessagePattern
Q_DECLARE_TYPEINFO(QMessagePattern::BacktraceParams, Q_RELOCATABLE_TYPE);
#endif
-QBasicMutex QMessagePattern::mutex;
+Q_CONSTINIT QBasicMutex QMessagePattern::mutex;
QMessagePattern::QMessagePattern()
{
@@ -1109,7 +1162,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 +1177,7 @@ void QMessagePattern::setPattern(const QString &pattern)
timeArgs.clear();
#ifdef QLOGGING_HAVE_BACKTRACE
backtraceArgs.clear();
+ maxBacktraceDepth = 0;
#endif
// scanner
@@ -1132,10 +1186,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 +1200,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 +1221,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 +1261,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 +1272,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 +1293,139 @@ 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;
+ 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 +1433,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 +1500,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 +1515,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 +1536,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,39 +1549,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) {
- message.append(QLatin1String(context.category));
+#ifndef Q_OS_ANDROID
+ // Don't add the category to the message on Android
+ 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) {
@@ -1440,24 +1590,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()) {
@@ -1466,7 +1616,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;
@@ -1480,21 +1629,35 @@ 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 -------------------------
#if defined(QT_BOOTSTRAPPED)
- // Boostrapped tools always print to stderr, so no need for alternate sinks
+ // Bootstrapped tools always print to stderr, so no need for alternate sinks
#else
#if QT_CONFIG(slog2)
@@ -1502,13 +1665,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;
@@ -1529,7 +1693,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:
@@ -1558,13 +1722,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:
@@ -1597,13 +1759,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:
@@ -1629,16 +1790,14 @@ static bool syslog_default_message_handler(QtMsgType type, const QMessageLogCont
}
#endif
-#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
+#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:
@@ -1658,17 +1817,21 @@ static bool android_default_message_handler(QtMsgType type,
break;
};
- __android_log_print(priority, qPrintable(QCoreApplication::applicationName()), "%s\n", qPrintable(formattedMessage));
+ // 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().replace(u' ', u'_')) : context.category,
+ "%s\n", qPrintable(formattedMessage));
return true; // Prevent further output to stderr
}
#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) {
@@ -1676,8 +1839,8 @@ static void win_outputDebugString_helper(QStringView message)
} else {
wchar_t *messagePart = new wchar_t[maxOutputStringLength + 1];
for (qsizetype i = 0; i < message.length(); i += maxOutputStringLength) {
- const qsizetype length = std::min(message.length() - i, maxOutputStringLength);
- const qsizetype len = message.mid(i, length).toWCharArray(messagePart);
+ const qsizetype length = qMin(message.length() - i, maxOutputStringLength);
+ const qsizetype len = QStringView{message}.mid(i, length).toWCharArray(messagePart);
Q_ASSERT(len == length);
messagePart[len] = 0;
OutputDebugString(messagePart);
@@ -1686,13 +1849,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
}
@@ -1700,15 +1863,14 @@ 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
- 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;
@@ -1733,58 +1895,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) && !defined(Q_OS_ANDROID_EMBEDDED)
- handledStderr |= android_default_message_handler(type, context, message);
-# elif defined(QT_USE_APPLE_UNIFIED_LOGGING)
- handledStderr |= AppleUnifiedLogger::messageHandler(type, context, message);
-# elif defined Q_OS_WASM
- handledStderr |= wasm_default_message_handler(type, context, message);
-# endif
-#endif
+ if (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()
{
@@ -1826,25 +2019,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
@@ -1863,45 +2045,24 @@ static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, const
_CrtDbgBreak();
#else
Q_UNUSED(context);
- Q_UNUSED(message);
#endif
-#ifdef Q_OS_WIN
- // std::abort() in the MSVC runtime will call _exit(3) if the abort
- // behavior is _WRITE_ABORT_MSG - see also _set_abort_behavior(). This is
- // the default for a debug-mode build of the runtime. Worse, MinGW's
- // std::abort() implementation (in msvcrt.dll) is basically a call to
- // _exit(3) too. Unfortunately, _exit() and _Exit() *do* run the static
- // destructors of objects in DLLs, a violation of the C++ standard (see
- // [support.start.term]). So we bypass std::abort() and directly
- // terminate the application.
-
-# if defined(Q_CC_MSVC) && !defined(Q_CC_INTEL)
- if (IsProcessorFeaturePresent(PF_FASTFAIL_AVAILABLE))
- __fastfail(FAST_FAIL_FATAL_APP_EXIT);
-# else
- RaiseFailFastException(nullptr, nullptr, 0);
-# endif
-
- // Fallback
- TerminateProcess(GetCurrentProcess(), STATUS_FATAL_APP_EXIT);
-
- // Tell the compiler the application has stopped.
- Q_UNREACHABLE_IMPL();
-#else // !Q_OS_WIN
- std::abort();
-#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, ...)
@@ -1915,9 +2076,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, ...)
@@ -1929,14 +2090,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
@@ -1949,41 +2110,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.
@@ -2016,9 +2205,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
@@ -2033,19 +2225,23 @@ 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
set, the environment variable takes precedence.
+ \note The information for the placeholders \c category, \c file, \c function and \c line is
+ only recorded in debug builds. Alternatively, \c QT_MESSAGELOGCONTEXT can be defined
+ explicitly. For more information refer to the QMessageLogContext documentation.
+
\note The message pattern only applies to unstructured logging, such as the default
\c stderr output. Structured logging such as systemd will record the message as is,
along with as much structured information as can be captured.
Custom message handlers can use qFormatLogMessage() to take \a pattern into account.
- \sa qInstallMessageHandler(), {Debugging Techniques}, {QLoggingCategory}
+ \sa qInstallMessageHandler(), {Debugging Techniques}, {QLoggingCategory}, QMessageLogContext
*/
QtMessageHandler qInstallMessageHandler(QtMessageHandler h)
@@ -2057,6 +2253,7 @@ QtMessageHandler qInstallMessageHandler(QtMessageHandler h)
return qDefaultMessageHandler;
}
+#ifndef QT_BOOTSTRAPPED
void qSetMessagePattern(const QString &pattern)
{
const auto locker = qt_scoped_lock(QMessagePattern::mutex);
@@ -2064,7 +2261,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.
@@ -2080,6 +2308,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;
}
@@ -2127,4 +2357,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