| /**************************************************************************** |
| ** |
| ** 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$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qglobal_p.h" |
| #include "qlogging.h" |
| #include "qlogging_p.h" |
| #include "qlist.h" |
| #include "qbytearray.h" |
| #include "qscopeguard.h" |
| #include "qstring.h" |
| #include "qvarlengtharray.h" |
| #include "qdebug.h" |
| #include "qmutex.h" |
| #include <QtCore/private/qlocking_p.h> |
| #include "qloggingcategory.h" |
| #ifndef QT_BOOTSTRAPPED |
| #include "qelapsedtimer.h" |
| #include "qdatetime.h" |
| #include "qcoreapplication.h" |
| #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 |
| #include <qt_windows.h> |
| #endif |
| #ifdef Q_CC_MSVC |
| #include <intrin.h> |
| #endif |
| #if QT_CONFIG(slog2) |
| #include <sys/slog2.h> |
| #endif |
| #if __has_include(<paths.h>) |
| #include <paths.h> |
| #endif |
| |
| #ifdef Q_OS_ANDROID |
| #include <android/log.h> |
| #endif |
| |
| #ifdef Q_OS_DARWIN |
| #include <QtCore/private/qcore_mac_p.h> |
| #endif |
| |
| #if QT_CONFIG(journald) |
| # define SD_JOURNAL_SUPPRESS_LOCATION |
| # include <systemd/sd-journal.h> |
| # include <syslog.h> |
| #endif |
| #if QT_CONFIG(syslog) |
| # include <syslog.h> |
| #endif |
| #ifdef Q_OS_UNIX |
| # include <sys/types.h> |
| # include <sys/stat.h> |
| # include <unistd.h> |
| # include "private/qcore_unix_p.h" |
| #endif |
| |
| #ifdef Q_OS_WASM |
| #include <emscripten/emscripten.h> |
| #endif |
| |
| #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 |
| # endif |
| #endif |
| |
| #if QT_CONFIG(slog2) |
| extern char *__progname; |
| #endif |
| |
| #ifndef QT_BOOTSTRAPPED |
| #if defined(Q_OS_LINUX) && (defined(__GLIBC__) || __has_include(<sys/syscall.h>)) |
| # include <sys/syscall.h> |
| |
| # if defined(Q_OS_ANDROID) && !defined(SYS_gettid) |
| # define SYS_gettid __NR_gettid |
| # endif |
| |
| static long qt_gettid() |
| { |
| // no error handling |
| // this syscall has existed since Linux 2.4.11 and cannot fail |
| return syscall(SYS_gettid); |
| } |
| #elif defined(Q_OS_DARWIN) |
| # include <pthread.h> |
| static int qt_gettid() |
| { |
| // no error handling: this call cannot fail |
| __uint64_t tid; |
| pthread_threadid_np(NULL, &tid); |
| return tid; |
| } |
| #elif defined(Q_OS_FREEBSD_KERNEL) && defined(__FreeBSD_version) && __FreeBSD_version >= 900031 |
| # include <pthread_np.h> |
| static int qt_gettid() |
| { |
| return pthread_getthreadid_np(); |
| } |
| #else |
| static QT_PREPEND_NAMESPACE(qint64) qt_gettid() |
| { |
| QT_USE_NAMESPACE |
| return qintptr(QThread::currentThreadId()); |
| } |
| #endif |
| |
| #ifdef QLOGGING_HAVE_BACKTRACE |
| # include <qregularexpression.h> |
| # include <cxxabi.h> |
| # include <execinfo.h> |
| #endif |
| #endif // !QT_BOOTSTRAPPED |
| |
| #include <cstdlib> |
| #include <algorithm> |
| #include <memory> |
| #include <vector> |
| |
| #include <stdio.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| #if !defined(Q_CC_MSVC) |
| Q_NORETURN |
| #endif |
| static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, const QString &message); |
| static void qt_message_print(QtMsgType, const QMessageLogContext &context, const QString &message); |
| static void qt_message_print(const QString &message); |
| |
| static int checked_var_value(const char *varname) |
| { |
| // qEnvironmentVariableIntValue returns 0 on both parsing failure and on |
| // empty, but we need to distinguish between the two for backwards |
| // compatibility reasons. |
| QByteArray str = qgetenv(varname); |
| if (str.isEmpty()) |
| return 0; |
| |
| bool ok; |
| int value = str.toInt(&ok, 0); |
| return ok ? value : 1; |
| } |
| |
| static bool isFatal(QtMsgType msgType) |
| { |
| if (msgType == QtFatalMsg) |
| return true; |
| |
| 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; |
| } |
| |
| 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 false; |
| } |
| |
| static bool isDefaultCategory(const char *category) |
| { |
| return !category || strcmp(category, "default") == 0; |
| } |
| |
| /*! |
| Returns true if writing to \c stderr is supported. |
| |
| \internal |
| \sa stderrHasConsoleAttached() |
| */ |
| static bool systemHasStderr() |
| { |
| #if defined(Q_OS_WINRT) |
| return false; // WinRT has no stderr |
| #endif |
| |
| return true; |
| } |
| |
| /*! |
| Returns true if writing to \c stderr will end up in a console/terminal visible to the user. |
| |
| This is typically the case if the application was started from the command line. |
| |
| If the application is started without a controlling console/terminal, but the parent |
| process reads \c stderr and presents it to the user in some other way, the parent process |
| may override the detection in this function by setting the QT_ASSUME_STDERR_HAS_CONSOLE |
| environment variable to \c 1. |
| |
| \note Qt Creator does not implement a pseudo TTY, nor does it launch apps with |
| the override environment variable set, but it will read stderr and print it to |
| the user, so in effect this function cannot be used to conclude that stderr |
| output will _not_ be visible to the user, as even if this function returns false, |
| the output might still end up visible to the user. For this reason, we don't guard |
| the stderr output in the default message handler with stderrHasConsoleAttached(). |
| |
| \internal |
| \sa systemHasStderr() |
| */ |
| static bool stderrHasConsoleAttached() |
| { |
| static const bool stderrHasConsoleAttached = []() -> bool { |
| if (!systemHasStderr()) |
| return false; |
| |
| if (qEnvironmentVariableIntValue("QT_LOGGING_TO_CONSOLE")) { |
| fprintf(stderr, "warning: Environment variable QT_LOGGING_TO_CONSOLE is deprecated, use\n" |
| "QT_ASSUME_STDERR_HAS_CONSOLE and/or QT_FORCE_STDERR_LOGGING instead.\n"); |
| return true; |
| } |
| |
| if (qEnvironmentVariableIntValue("QT_ASSUME_STDERR_HAS_CONSOLE")) |
| return true; |
| |
| #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) |
| return GetConsoleWindow(); |
| #elif defined(Q_OS_UNIX) |
| # ifndef _PATH_TTY |
| # define _PATH_TTY "/dev/tty" |
| # endif |
| |
| // If we can open /dev/tty, we have a controlling TTY |
| int ttyDevice = -1; |
| if ((ttyDevice = qt_safe_open(_PATH_TTY, O_RDONLY)) >= 0) { |
| qt_safe_close(ttyDevice); |
| return true; |
| } else if (errno == ENOENT || errno == EPERM || errno == ENXIO) { |
| // Fall back to isatty for some non-critical errors |
| return isatty(STDERR_FILENO); |
| } else { |
| return false; |
| } |
| #else |
| return false; // No way to detect if stderr has a console attached |
| #endif |
| }(); |
| |
| return stderrHasConsoleAttached; |
| } |
| |
| |
| namespace QtPrivate { |
| |
| /*! |
| Returns true if logging \c stderr should be ensured. |
| |
| This is normally the case if \c stderr has a console attached, but may be overridden |
| by the user by setting the QT_FORCE_STDERR_LOGGING environment variable to \c 1. |
| |
| \internal |
| \sa stderrHasConsoleAttached() |
| */ |
| bool shouldLogToStderr() |
| { |
| static bool forceStderrLogging = qEnvironmentVariableIntValue("QT_FORCE_STDERR_LOGGING"); |
| return forceStderrLogging || stderrHasConsoleAttached(); |
| } |
| |
| |
| } // QtPrivate |
| |
| using namespace QtPrivate; |
| |
| /*! |
| \class QMessageLogContext |
| \inmodule QtCore |
| \brief The QMessageLogContext class provides additional information about a log message. |
| \since 5.0 |
| |
| The class provides information about the source code location a qDebug(), qInfo(), qWarning(), |
| qCritical() or qFatal() message was generated. |
| |
| \note By default, this information is recorded only in debug builds. You can overwrite |
| this explicitly by defining \c QT_MESSAGELOGCONTEXT or \c{QT_NO_MESSAGELOGCONTEXT}. |
| |
| \sa QMessageLogger, QtMessageHandler, qInstallMessageHandler() |
| */ |
| |
| /*! |
| \class QMessageLogger |
| \inmodule QtCore |
| \brief The QMessageLogger class generates log messages. |
| \since 5.0 |
| |
| QMessageLogger is used to generate messages for the Qt logging framework. Usually one uses |
| it through qDebug(), qInfo(), qWarning(), qCritical, or qFatal() functions, |
| which are actually macros: For example qDebug() expands to |
| QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).debug() |
| for debug builds, and QMessageLogger(0, 0, 0).debug() for release builds. |
| |
| One example of direct use is to forward errors that stem from a scripting language, e.g. QML: |
| |
| \snippet code/qlogging/qlogging.cpp 1 |
| |
| \sa QMessageLogContext, qDebug(), qInfo(), qWarning(), qCritical(), qFatal() |
| */ |
| |
| #if defined(Q_CC_MSVC) && 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); |
| if (len + 1 > space) { |
| const size_t skip = len - space + 4; // 4 for "..." + '\0' |
| s += skip; |
| len -= skip; |
| for (int i = 0; i < 3; ++i) |
| *d++ = L'.'; |
| } |
| while (len--) |
| *d++ = *s++; |
| *d++ = 0; |
| } |
| #endif |
| |
| /*! |
| \internal |
| */ |
| Q_NEVER_INLINE |
| static QString 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; |
| } |
| |
| #undef qDebug |
| /*! |
| Logs a debug message specified with format \a msg. Additional |
| parameters, specified by \a msg, may be used. |
| |
| \sa qDebug() |
| */ |
| void QMessageLogger::debug(const char *msg, ...) const |
| { |
| va_list ap; |
| va_start(ap, msg); // use variable arg list |
| const QString message = qt_message(QtDebugMsg, context, msg, ap); |
| 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. |
| |
| \sa qInfo() |
| \since 5.5 |
| */ |
| void QMessageLogger::info(const char *msg, ...) const |
| { |
| va_list ap; |
| va_start(ap, msg); // use variable arg list |
| const QString message = qt_message(QtInfoMsg, context, msg, ap); |
| va_end(ap); |
| |
| if (isFatal(QtInfoMsg)) |
| qt_message_fatal(QtInfoMsg, context, message); |
| } |
| |
| /*! |
| \typedef QMessageLogger::CategoryFunction |
| |
| This is a typedef for a pointer to a function with the following |
| signature: |
| |
| \snippet code/qlogging/qlogging.cpp 2 |
| |
| A function which this signature is generated by Q_DECLARE_LOGGING_CATEGORY, |
| Q_LOGGING_CATEGORY. |
| |
| \since 5.3 |
| */ |
| |
| /*! |
| Logs a debug message specified with format \a msg for the context \a cat. |
| Additional parameters, specified by \a msg, may be used. |
| |
| \since 5.3 |
| \sa qCDebug() |
| */ |
| void QMessageLogger::debug(const QLoggingCategory &cat, const char *msg, ...) const |
| { |
| if (!cat.isDebugEnabled()) |
| return; |
| |
| QMessageLogContext ctxt; |
| ctxt.copyContextFrom(context); |
| ctxt.category = cat.categoryName(); |
| |
| va_list ap; |
| va_start(ap, msg); // use variable arg list |
| const QString message = qt_message(QtDebugMsg, ctxt, msg, ap); |
| va_end(ap); |
| |
| if (isFatal(QtDebugMsg)) |
| qt_message_fatal(QtDebugMsg, ctxt, message); |
| } |
| |
| /*! |
| Logs a debug message specified with format \a msg for the context returned |
| by \a catFunc. Additional parameters, specified by \a msg, may be used. |
| |
| \since 5.3 |
| \sa qCDebug() |
| */ |
| void QMessageLogger::debug(QMessageLogger::CategoryFunction catFunc, |
| const char *msg, ...) const |
| { |
| const QLoggingCategory &cat = (*catFunc)(); |
| if (!cat.isDebugEnabled()) |
| return; |
| |
| QMessageLogContext ctxt; |
| ctxt.copyContextFrom(context); |
| ctxt.category = cat.categoryName(); |
| |
| va_list ap; |
| va_start(ap, msg); // use variable arg list |
| const QString message = qt_message(QtDebugMsg, ctxt, msg, ap); |
| va_end(ap); |
| |
| if (isFatal(QtDebugMsg)) |
| qt_message_fatal(QtDebugMsg, ctxt, message); |
| } |
| |
| #ifndef QT_NO_DEBUG_STREAM |
| |
| /*! |
| Logs a debug message using a QDebug stream |
| |
| \sa qDebug(), QDebug |
| */ |
| QDebug QMessageLogger::debug() const |
| { |
| QDebug dbg = QDebug(QtDebugMsg); |
| QMessageLogContext &ctxt = dbg.stream->context; |
| ctxt.copyContextFrom(context); |
| return dbg; |
| } |
| |
| /*! |
| Logs a debug message into category \a cat using a QDebug stream. |
| |
| \since 5.3 |
| \sa qCDebug(), QDebug |
| */ |
| QDebug QMessageLogger::debug(const QLoggingCategory &cat) const |
| { |
| QDebug dbg = QDebug(QtDebugMsg); |
| if (!cat.isDebugEnabled()) |
| dbg.stream->message_output = false; |
| |
| QMessageLogContext &ctxt = dbg.stream->context; |
| ctxt.copyContextFrom(context); |
| ctxt.category = cat.categoryName(); |
| |
| return dbg; |
| } |
| |
| /*! |
| Logs a debug message into category returned by \a catFunc using a QDebug stream. |
| |
| \since 5.3 |
| \sa qCDebug(), QDebug |
| */ |
| QDebug QMessageLogger::debug(QMessageLogger::CategoryFunction catFunc) const |
| { |
| return debug((*catFunc)()); |
| } |
| |
| /*! |
| \internal |
| |
| Returns a QNoDebug object, which is used to ignore debugging output. |
| |
| \sa QNoDebug, qDebug() |
| */ |
| QNoDebug QMessageLogger::noDebug() const noexcept |
| { |
| return QNoDebug(); |
| } |
| |
| #endif |
| |
| /*! |
| Logs an informational message specified with format \a msg for the context \a cat. |
| Additional parameters, specified by \a msg, may be used. |
| |
| \since 5.5 |
| \sa qCInfo() |
| */ |
| void QMessageLogger::info(const QLoggingCategory &cat, const char *msg, ...) const |
| { |
| if (!cat.isInfoEnabled()) |
| return; |
| |
| QMessageLogContext ctxt; |
| ctxt.copyContextFrom(context); |
| ctxt.category = cat.categoryName(); |
| |
| va_list ap; |
| va_start(ap, msg); // use variable arg list |
| const QString message = qt_message(QtInfoMsg, ctxt, msg, ap); |
| va_end(ap); |
| |
| if (isFatal(QtInfoMsg)) |
| qt_message_fatal(QtInfoMsg, ctxt, message); |
| } |
| |
| /*! |
| Logs an informational message specified with format \a msg for the context returned |
| by \a catFunc. Additional parameters, specified by \a msg, may be used. |
| |
| \since 5.5 |
| \sa qCInfo() |
| */ |
| void QMessageLogger::info(QMessageLogger::CategoryFunction catFunc, |
| const char *msg, ...) const |
| { |
| const QLoggingCategory &cat = (*catFunc)(); |
| if (!cat.isInfoEnabled()) |
| return; |
| |
| QMessageLogContext ctxt; |
| ctxt.copyContextFrom(context); |
| ctxt.category = cat.categoryName(); |
| |
| va_list ap; |
| va_start(ap, msg); // use variable arg list |
| const QString message = qt_message(QtInfoMsg, ctxt, msg, ap); |
| va_end(ap); |
| |
| if (isFatal(QtInfoMsg)) |
| qt_message_fatal(QtInfoMsg, ctxt, message); |
| } |
| |
| #ifndef QT_NO_DEBUG_STREAM |
| |
| /*! |
| Logs an informational message using a QDebug stream. |
| |
| \since 5.5 |
| \sa qInfo(), QDebug |
| */ |
| QDebug QMessageLogger::info() const |
| { |
| QDebug dbg = QDebug(QtInfoMsg); |
| QMessageLogContext &ctxt = dbg.stream->context; |
| ctxt.copyContextFrom(context); |
| return dbg; |
| } |
| |
| /*! |
| Logs an informational message into the category \a cat using a QDebug stream. |
| |
| \since 5.5 |
| \sa qCInfo(), QDebug |
| */ |
| QDebug QMessageLogger::info(const QLoggingCategory &cat) const |
| { |
| QDebug dbg = QDebug(QtInfoMsg); |
| if (!cat.isInfoEnabled()) |
| dbg.stream->message_output = false; |
| |
| QMessageLogContext &ctxt = dbg.stream->context; |
| ctxt.copyContextFrom(context); |
| ctxt.category = cat.categoryName(); |
| |
| return dbg; |
| } |
| |
| /*! |
| Logs an informational message into category returned by \a catFunc using a QDebug stream. |
| |
| \since 5.5 |
| \sa qCInfo(), QDebug |
| */ |
| QDebug QMessageLogger::info(QMessageLogger::CategoryFunction catFunc) const |
| { |
| return info((*catFunc)()); |
| } |
| |
| #endif |
| |
| #undef qWarning |
| /*! |
| Logs a warning message specified with format \a msg. Additional |
| parameters, specified by \a msg, may be used. |
| |
| \sa qWarning() |
| */ |
| void QMessageLogger::warning(const char *msg, ...) const |
| { |
| va_list ap; |
| va_start(ap, msg); // use variable arg list |
| const QString message = qt_message(QtWarningMsg, context, msg, ap); |
| va_end(ap); |
| |
| if (isFatal(QtWarningMsg)) |
| qt_message_fatal(QtWarningMsg, context, message); |
| } |
| |
| /*! |
| Logs a warning message specified with format \a msg for the context \a cat. |
| Additional parameters, specified by \a msg, may be used. |
| |
| \since 5.3 |
| \sa qCWarning() |
| */ |
| void QMessageLogger::warning(const QLoggingCategory &cat, const char *msg, ...) const |
| { |
| if (!cat.isWarningEnabled()) |
| return; |
| |
| QMessageLogContext ctxt; |
| ctxt.copyContextFrom(context); |
| ctxt.category = cat.categoryName(); |
| |
| va_list ap; |
| va_start(ap, msg); // use variable arg list |
| const QString message = qt_message(QtWarningMsg, ctxt, msg, ap); |
| va_end(ap); |
| |
| if (isFatal(QtWarningMsg)) |
| qt_message_fatal(QtWarningMsg, ctxt, message); |
| } |
| |
| /*! |
| Logs a warning message specified with format \a msg for the context returned |
| by \a catFunc. Additional parameters, specified by \a msg, may be used. |
| |
| \since 5.3 |
| \sa qCWarning() |
| */ |
| void QMessageLogger::warning(QMessageLogger::CategoryFunction catFunc, |
| const char *msg, ...) const |
| { |
| const QLoggingCategory &cat = (*catFunc)(); |
| if (!cat.isWarningEnabled()) |
| return; |
| |
| QMessageLogContext ctxt; |
| ctxt.copyContextFrom(context); |
| ctxt.category = cat.categoryName(); |
| |
| va_list ap; |
| va_start(ap, msg); // use variable arg list |
| const QString message = qt_message(QtWarningMsg, ctxt, msg, ap); |
| va_end(ap); |
| |
| if (isFatal(QtWarningMsg)) |
| qt_message_fatal(QtWarningMsg, ctxt, message); |
| } |
| |
| #ifndef QT_NO_DEBUG_STREAM |
| /*! |
| Logs a warning message using a QDebug stream |
| |
| \sa qWarning(), QDebug |
| */ |
| QDebug QMessageLogger::warning() const |
| { |
| QDebug dbg = QDebug(QtWarningMsg); |
| QMessageLogContext &ctxt = dbg.stream->context; |
| ctxt.copyContextFrom(context); |
| return dbg; |
| } |
| |
| /*! |
| Logs a warning message into category \a cat using a QDebug stream. |
| |
| \sa qCWarning(), QDebug |
| */ |
| QDebug QMessageLogger::warning(const QLoggingCategory &cat) const |
| { |
| QDebug dbg = QDebug(QtWarningMsg); |
| if (!cat.isWarningEnabled()) |
| dbg.stream->message_output = false; |
| |
| QMessageLogContext &ctxt = dbg.stream->context; |
| ctxt.copyContextFrom(context); |
| ctxt.category = cat.categoryName(); |
| |
| return dbg; |
| } |
| |
| /*! |
| Logs a warning message into category returned by \a catFunc using a QDebug stream. |
| |
| \since 5.3 |
| \sa qCWarning(), QDebug |
| */ |
| QDebug QMessageLogger::warning(QMessageLogger::CategoryFunction catFunc) const |
| { |
| return warning((*catFunc)()); |
| } |
| |
| #endif |
| |
| #undef qCritical |
| |
| /*! |
| Logs a critical message specified with format \a msg. Additional |
| parameters, specified by \a msg, may be used. |
| |
| \sa qCritical() |
| */ |
| void QMessageLogger::critical(const char *msg, ...) const |
| { |
| va_list ap; |
| va_start(ap, msg); // use variable arg list |
| const QString message = qt_message(QtCriticalMsg, context, msg, ap); |
| va_end(ap); |
| |
| if (isFatal(QtCriticalMsg)) |
| qt_message_fatal(QtCriticalMsg, context, message); |
| } |
| |
| /*! |
| Logs a critical message specified with format \a msg for the context \a cat. |
| Additional parameters, specified by \a msg, may be used. |
| |
| \since 5.3 |
| \sa qCCritical() |
| */ |
| void QMessageLogger::critical(const QLoggingCategory &cat, const char *msg, ...) const |
| { |
| if (!cat.isCriticalEnabled()) |
| return; |
| |
| QMessageLogContext ctxt; |
| ctxt.copyContextFrom(context); |
| ctxt.category = cat.categoryName(); |
| |
| va_list ap; |
| va_start(ap, msg); // use variable arg list |
| const QString message = qt_message(QtCriticalMsg, ctxt, msg, ap); |
| va_end(ap); |
| |
| if (isFatal(QtCriticalMsg)) |
| qt_message_fatal(QtCriticalMsg, ctxt, message); |
| } |
| |
| /*! |
| Logs a critical message specified with format \a msg for the context returned |
| by \a catFunc. Additional parameters, specified by \a msg, may be used. |
| |
| \since 5.3 |
| \sa qCCritical() |
| */ |
| void QMessageLogger::critical(QMessageLogger::CategoryFunction catFunc, |
| const char *msg, ...) const |
| { |
| const QLoggingCategory &cat = (*catFunc)(); |
| if (!cat.isCriticalEnabled()) |
| return; |
| |
| QMessageLogContext ctxt; |
| ctxt.copyContextFrom(context); |
| ctxt.category = cat.categoryName(); |
| |
| va_list ap; |
| va_start(ap, msg); // use variable arg list |
| const QString message = qt_message(QtCriticalMsg, ctxt, msg, ap); |
| va_end(ap); |
| |
| if (isFatal(QtCriticalMsg)) |
| qt_message_fatal(QtCriticalMsg, ctxt, message); |
| } |
| |
| #ifndef QT_NO_DEBUG_STREAM |
| /*! |
| Logs a critical message using a QDebug stream |
| |
| \sa qCritical(), QDebug |
| */ |
| QDebug QMessageLogger::critical() const |
| { |
| QDebug dbg = QDebug(QtCriticalMsg); |
| QMessageLogContext &ctxt = dbg.stream->context; |
| ctxt.copyContextFrom(context); |
| return dbg; |
| } |
| |
| /*! |
| Logs a critical message into category \a cat using a QDebug stream. |
| |
| \since 5.3 |
| \sa qCCritical(), QDebug |
| */ |
| QDebug QMessageLogger::critical(const QLoggingCategory &cat) const |
| { |
| QDebug dbg = QDebug(QtCriticalMsg); |
| if (!cat.isCriticalEnabled()) |
| dbg.stream->message_output = false; |
| |
| QMessageLogContext &ctxt = dbg.stream->context; |
| ctxt.copyContextFrom(context); |
| ctxt.category = cat.categoryName(); |
| |
| return dbg; |
| } |
| |
| /*! |
| Logs a critical message into category returned by \a catFunc using a QDebug stream. |
| |
| \since 5.3 |
| \sa qCCritical(), QDebug |
| */ |
| QDebug QMessageLogger::critical(QMessageLogger::CategoryFunction catFunc) const |
| { |
| return critical((*catFunc)()); |
| } |
| |
| #endif |
| |
| #undef qFatal |
| /*! |
| Logs a fatal message specified with format \a msg. Additional |
| parameters, specified by \a msg, may be used. |
| |
| \sa qFatal() |
| */ |
| void QMessageLogger::fatal(const char *msg, ...) const noexcept |
| { |
| QString message; |
| |
| va_list ap; |
| va_start(ap, msg); // use variable arg list |
| QT_TERMINATE_ON_EXCEPTION(message = qt_message(QtFatalMsg, context, msg, ap)); |
| va_end(ap); |
| |
| qt_message_fatal(QtFatalMsg, context, message); |
| } |
| |
| /*! |
| \internal |
| */ |
| Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info) |
| { |
| // Strip the function info down to the base function name |
| // note that this throws away the template definitions, |
| // the parameter types (overloads) and any const/volatile qualifiers. |
| |
| if (info.isEmpty()) |
| return info; |
| |
| int 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); |
| } |
| } |
| |
| // operator names with '(', ')', '<', '>' in it |
| static const char operator_call[] = "operator()"; |
| static const char operator_lessThan[] = "operator<"; |
| static const char operator_greaterThan[] = "operator>"; |
| static const char operator_lessThanEqual[] = "operator<="; |
| static const char operator_greaterThanEqual[] = "operator>="; |
| |
| // canonize operator names |
| info.replace("operator ", "operator"); |
| |
| // remove argument list |
| forever { |
| int parencount = 0; |
| pos = info.lastIndexOf(')'); |
| if (pos == -1) { |
| // Don't know how to parse this function name |
| return info; |
| } |
| |
| // find the beginning of the argument list |
| --pos; |
| ++parencount; |
| while (pos && parencount) { |
| if (info.at(pos) == ')') |
| ++parencount; |
| else if (info.at(pos) == '(') |
| --parencount; |
| --pos; |
| } |
| if (parencount != 0) |
| return info; |
| |
| info.truncate(++pos); |
| |
| if (info.at(pos - 1) == ')') { |
| if (info.indexOf(operator_call) == pos - (int)strlen(operator_call)) |
| break; |
| |
| // this function returns a pointer to a function |
| // and we matched the arguments of the return type's parameter list |
| // try again |
| info.remove(0, info.indexOf('(')); |
| info.chop(1); |
| continue; |
| } else { |
| break; |
| } |
| } |
| |
| // find the beginning of the function name |
| int parencount = 0; |
| int templatecount = 0; |
| --pos; |
| |
| // make sure special characters in operator names are kept |
| if (pos > -1) { |
| switch (info.at(pos)) { |
| case ')': |
| if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1) |
| pos -= 2; |
| break; |
| case '<': |
| if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1) |
| --pos; |
| break; |
| case '>': |
| if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1) |
| --pos; |
| break; |
| case '=': { |
| int operatorLength = (int)strlen(operator_lessThanEqual); |
| if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1) |
| pos -= 2; |
| else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1) |
| pos -= 2; |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| while (pos > -1) { |
| if (parencount < 0 || templatecount < 0) |
| return info; |
| |
| char c = info.at(pos); |
| if (c == ')') |
| ++parencount; |
| else if (c == '(') |
| --parencount; |
| else if (c == '>') |
| ++templatecount; |
| else if (c == '<') |
| --templatecount; |
| else if (c == ' ' && templatecount == 0 && parencount == 0) |
| break; |
| |
| --pos; |
| } |
| info = info.mid(pos + 1); |
| |
| // remove trailing '*', '&' that are part of the return argument |
| while ((info.at(0) == '*') |
| || (info.at(0) == '&')) |
| info = info.mid(1); |
| |
| // we have the full function name now. |
| // clean up the templates |
| while ((pos = info.lastIndexOf('>')) != -1) { |
| if (!info.contains('<')) |
| break; |
| |
| // find the matching close |
| int end = pos; |
| templatecount = 1; |
| --pos; |
| while (pos && templatecount) { |
| char c = info.at(pos); |
| if (c == '>') |
| ++templatecount; |
| else if (c == '<') |
| --templatecount; |
| --pos; |
| } |
| ++pos; |
| info.remove(pos, end - pos + 1); |
| } |
| |
| return info; |
| } |
| |
| // tokens as recognized in QT_MESSAGE_PATTERN |
| static const char categoryTokenC[] = "%{category}"; |
| static const char typeTokenC[] = "%{type}"; |
| static const char messageTokenC[] = "%{message}"; |
| static const char fileTokenC[] = "%{file}"; |
| static const char lineTokenC[] = "%{line}"; |
| static const char functionTokenC[] = "%{function}"; |
| static const char pidTokenC[] = "%{pid}"; |
| static const char appnameTokenC[] = "%{appname}"; |
| static const char threadidTokenC[] = "%{threadid}"; |
| static const char qthreadptrTokenC[] = "%{qthreadptr}"; |
| static const char timeTokenC[] = "%{time"; //not a typo: this command has arguments |
| static const char backtraceTokenC[] = "%{backtrace"; //ditto |
| static const char ifCategoryTokenC[] = "%{if-category}"; |
| static const char ifDebugTokenC[] = "%{if-debug}"; |
| static const char ifInfoTokenC[] = "%{if-info}"; |
| static const char ifWarningTokenC[] = "%{if-warning}"; |
| static const char ifCriticalTokenC[] = "%{if-critical}"; |
| static const char ifFatalTokenC[] = "%{if-fatal}"; |
| static const char endifTokenC[] = "%{endif}"; |
| static const char emptyTokenC[] = ""; |
| |
| static const char defaultPattern[] = "%{if-category}%{category}: %{endif}%{message}"; |
| |
| |
| struct QMessagePattern { |
| QMessagePattern(); |
| ~QMessagePattern(); |
| |
| void setPattern(const QString &pattern); |
| |
| // 0 terminated arrays of literal tokens / literal or placeholder tokens |
| std::unique_ptr<std::unique_ptr<const char[]>[]> literals; |
| std::unique_ptr<const char*[]> tokens; |
| QList<QString> timeArgs; // timeFormats in sequence of %{time |
| #ifndef QT_BOOTSTRAPPED |
| QElapsedTimer timer; |
| #endif |
| #ifdef QLOGGING_HAVE_BACKTRACE |
| struct BacktraceParams { |
| QString backtraceSeparator; |
| int backtraceDepth; |
| }; |
| QVector<BacktraceParams> backtraceArgs; // backtrace argumens in sequence of %{backtrace |
| #endif |
| |
| bool fromEnvironment; |
| static QBasicMutex mutex; |
| }; |
| #ifdef QLOGGING_HAVE_BACKTRACE |
| Q_DECLARE_TYPEINFO(QMessagePattern::BacktraceParams, Q_MOVABLE_TYPE); |
| #endif |
| |
| QBasicMutex QMessagePattern::mutex; |
| |
| QMessagePattern::QMessagePattern() |
| { |
| #ifndef QT_BOOTSTRAPPED |
| timer.start(); |
| #endif |
| const QString envPattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN")); |
| if (envPattern.isEmpty()) { |
| setPattern(QLatin1String(defaultPattern)); |
| fromEnvironment = false; |
| } else { |
| setPattern(envPattern); |
| fromEnvironment = true; |
| } |
| } |
| |
| QMessagePattern::~QMessagePattern() |
| = default; |
| |
| void QMessagePattern::setPattern(const QString &pattern) |
| { |
| timeArgs.clear(); |
| #ifdef QLOGGING_HAVE_BACKTRACE |
| backtraceArgs.clear(); |
| #endif |
| |
| // scanner |
| QList<QString> lexemes; |
| QString lexeme; |
| bool inPlaceholder = false; |
| for (int i = 0; i < pattern.size(); ++i) { |
| const QChar c = pattern.at(i); |
| if ((c == QLatin1Char('%')) |
| && !inPlaceholder) { |
| if ((i + 1 < pattern.size()) |
| && pattern.at(i + 1) == QLatin1Char('{')) { |
| // beginning of placeholder |
| if (!lexeme.isEmpty()) { |
| lexemes.append(lexeme); |
| lexeme.clear(); |
| } |
| inPlaceholder = true; |
| } |
| } |
| |
| lexeme.append(c); |
| |
| if ((c == QLatin1Char('}') && inPlaceholder)) { |
| // end of placeholder |
| lexemes.append(lexeme); |
| lexeme.clear(); |
| inPlaceholder = false; |
| } |
| } |
| if (!lexeme.isEmpty()) |
| lexemes.append(lexeme); |
| |
| // tokenizer |
| std::vector<std::unique_ptr<const char[]>> literalsVar; |
| tokens.reset(new const char*[lexemes.size() + 1]); |
| tokens[lexemes.size()] = nullptr; |
| |
| bool nestedIfError = false; |
| bool inIf = false; |
| QString error; |
| |
| for (int i = 0; i < lexemes.size(); ++i) { |
| const QString lexeme = lexemes.at(i); |
| if (lexeme.startsWith(QLatin1String("%{")) |
| && lexeme.endsWith(QLatin1Char('}'))) { |
| // placeholder |
| if (lexeme == QLatin1String(typeTokenC)) { |
| tokens[i] = typeTokenC; |
| } else if (lexeme == QLatin1String(categoryTokenC)) |
| tokens[i] = categoryTokenC; |
| else if (lexeme == QLatin1String(messageTokenC)) |
| tokens[i] = messageTokenC; |
| else if (lexeme == QLatin1String(fileTokenC)) |
| tokens[i] = fileTokenC; |
| else if (lexeme == QLatin1String(lineTokenC)) |
| tokens[i] = lineTokenC; |
| else if (lexeme == QLatin1String(functionTokenC)) |
| tokens[i] = functionTokenC; |
| else if (lexeme == QLatin1String(pidTokenC)) |
| tokens[i] = pidTokenC; |
| else if (lexeme == QLatin1String(appnameTokenC)) |
| tokens[i] = appnameTokenC; |
| else if (lexeme == QLatin1String(threadidTokenC)) |
| tokens[i] = threadidTokenC; |
| else if (lexeme == QLatin1String(qthreadptrTokenC)) |
| tokens[i] = qthreadptrTokenC; |
| else if (lexeme.startsWith(QLatin1String(timeTokenC))) { |
| tokens[i] = timeTokenC; |
| int spaceIdx = lexeme.indexOf(QChar::fromLatin1(' ')); |
| if (spaceIdx > 0) |
| timeArgs.append(lexeme.mid(spaceIdx + 1, lexeme.length() - spaceIdx - 2)); |
| else |
| timeArgs.append(QString()); |
| } else if (lexeme.startsWith(QLatin1String(backtraceTokenC))) { |
| #ifdef QLOGGING_HAVE_BACKTRACE |
| tokens[i] = backtraceTokenC; |
| QString backtraceSeparator = QStringLiteral("|"); |
| int backtraceDepth = 5; |
| static const QRegularExpression depthRx(QStringLiteral(" depth=(?|\"([^\"]*)\"|([^ }]*))")); |
| static const QRegularExpression separatorRx(QStringLiteral(" separator=(?|\"([^\"]*)\"|([^ }]*))")); |
| QRegularExpressionMatch m = depthRx.match(lexeme); |
| if (m.hasMatch()) { |
| int depth = m.capturedRef(1).toInt(); |
| if (depth <= 0) |
| error += QLatin1String("QT_MESSAGE_PATTERN: %{backtrace} depth must be a number greater than 0\n"); |
| else |
| backtraceDepth = depth; |
| } |
| m = separatorRx.match(lexeme); |
| if (m.hasMatch()) |
| backtraceSeparator = m.captured(1); |
| BacktraceParams backtraceParams; |
| backtraceParams.backtraceDepth = backtraceDepth; |
| backtraceParams.backtraceSeparator = backtraceSeparator; |
| backtraceArgs.append(backtraceParams); |
| #else |
| error += QLatin1String("QT_MESSAGE_PATTERN: %{backtrace} is not supported by this Qt build\n"); |
| tokens[i] = ""; |
| #endif |
| } |
| |
| #define IF_TOKEN(LEVEL) \ |
| else if (lexeme == QLatin1String(LEVEL)) { \ |
| if (inIf) \ |
| nestedIfError = true; \ |
| tokens[i] = LEVEL; \ |
| inIf = true; \ |
| } |
| IF_TOKEN(ifCategoryTokenC) |
| IF_TOKEN(ifDebugTokenC) |
| IF_TOKEN(ifInfoTokenC) |
| IF_TOKEN(ifWarningTokenC) |
| IF_TOKEN(ifCriticalTokenC) |
| IF_TOKEN(ifFatalTokenC) |
| #undef IF_TOKEN |
| else if (lexeme == QLatin1String(endifTokenC)) { |
| tokens[i] = endifTokenC; |
| if (!inIf && !nestedIfError) |
| error += QLatin1String("QT_MESSAGE_PATTERN: %{endif} without an %{if-*}\n"); |
| inIf = false; |
| } else { |
| tokens[i] = emptyTokenC; |
| error += QStringLiteral("QT_MESSAGE_PATTERN: Unknown placeholder %1\n") |
| .arg(lexeme); |
| } |
| } 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; |
| } |
| } |
| if (nestedIfError) |
| error += QLatin1String("QT_MESSAGE_PATTERN: %{if-*} cannot be nested\n"); |
| else if (inIf) |
| error += QLatin1String("QT_MESSAGE_PATTERN: missing %{endif}\n"); |
| |
| if (!error.isEmpty()) |
| qt_message_print(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) |
| // make sure the function has "Message" in the name so the function is removed |
| |
| #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) |
| { |
| QStringList result; |
| if (frameCount == 0) |
| return result; |
| |
| // The results of backtrace_symbols looks like this: |
| // /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413] |
| // The offset and function name are optional. |
| // This regexp tries to extract the library name (without the path) and the function name. |
| // 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("Qt5Core")) |
| && (function.isEmpty() || function.contains(QLatin1String("Message"), Qt::CaseInsensitive) |
| || function.contains(QLatin1String("QDebug")))) { |
| continue; |
| } |
| |
| if (function.startsWith(QLatin1String("_Z"))) { |
| QScopedPointer<char, QScopedPointerPodDeleter> demangled( |
| abi::__cxa_demangle(function.toUtf8(), 0, 0, 0)); |
| if (demangled) |
| function = QString::fromUtf8(qCleanupFuncinfo(demangled.data())); |
| } |
| |
| 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; |
| } |
| result.append(QStringLiteral("???")); |
| } |
| numberPrinted++; |
| } |
| } |
| return result; |
| } |
| |
| static QString formatBacktraceForLogMessage(const QMessagePattern::BacktraceParams backtraceParams, |
| const char *function) |
| { |
| QString backtraceSeparator = backtraceParams.backtraceSeparator; |
| int backtraceDepth = backtraceParams.backtraceDepth; |
| |
| QStringList frames = backtraceFramesForLogMessage(backtraceDepth); |
| 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)); |
| |
| return frames.join(backtraceSeparator); |
| } |
| #endif // QLOGGING_HAVE_BACKTRACE && !QT_BOOTSTRAPPED |
| |
| Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern) |
| |
| /*! |
| \relates <QtGlobal> |
| \since 5.4 |
| |
| Generates a formatted string out of the \a type, \a context, \a str arguments. |
| |
| qFormatLogMessage returns a QString that is formatted according to the current message pattern. |
| It can be used by custom message handlers to format output similar to Qt's default message |
| handler. |
| |
| The function is thread-safe. |
| |
| \sa qInstallMessageHandler(), qSetMessagePattern() |
| */ |
| QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str) |
| { |
| QString message; |
| |
| const auto locker = qt_scoped_lock(QMessagePattern::mutex); |
| |
| QMessagePattern *pattern = qMessagePattern(); |
| if (!pattern) { |
| // after destruction of static QMessagePattern instance |
| message.append(str); |
| return message; |
| } |
| |
| 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) { |
| const char *token = pattern->tokens[i]; |
| if (token == endifTokenC) { |
| skip = false; |
| } 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)); |
| } 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; |
| } |
| } else if (token == fileTokenC) { |
| if (context.file) |
| message.append(QLatin1String(context.file)); |
| else |
| message.append(QLatin1String("unknown")); |
| } 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 |
| } else if (token == pidTokenC) { |
| message.append(QString::number(QCoreApplication::applicationPid())); |
| } else if (token == appnameTokenC) { |
| message.append(QCoreApplication::applicationName()); |
| } else if (token == threadidTokenC) { |
| // print the TID as decimal |
| message.append(QString::number(qt_gettid())); |
| } else if (token == qthreadptrTokenC) { |
| message.append(QLatin1String("0x")); |
| 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)); |
| #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")) { |
| // just print the milliseconds since the elapsed timer reference |
| // like the Linux kernel does |
| QElapsedTimer now; |
| now.start(); |
| uint ms = now.msecsSinceReference(); |
| message.append(QString::asprintf("%6d.%03d", uint(ms / 1000), uint(ms % 1000))); |
| #if QT_CONFIG(datestring) |
| } else if (timeFormat.isEmpty()) { |
| message.append(QDateTime::currentDateTime().toString(Qt::ISODate)); |
| } else { |
| message.append(QDateTime::currentDateTime().toString(timeFormat)); |
| #endif // QT_CONFIG(datestring) |
| } |
| #endif // !QT_BOOTSTRAPPED |
| } else if (token == ifCategoryTokenC) { |
| if (isDefaultCategory(context.category)) |
| skip = true; |
| #define HANDLE_IF_TOKEN(LEVEL) \ |
| } else if (token == if##LEVEL##TokenC) { \ |
| skip = type != Qt##LEVEL##Msg; |
| HANDLE_IF_TOKEN(Debug) |
| HANDLE_IF_TOKEN(Info) |
| HANDLE_IF_TOKEN(Warning) |
| HANDLE_IF_TOKEN(Critical) |
| HANDLE_IF_TOKEN(Fatal) |
| #undef HANDLE_IF_TOKEN |
| } else { |
| message.append(QLatin1String(token)); |
| } |
| } |
| return message; |
| } |
| |
| #if !QT_DEPRECATED_SINCE(5, 0) |
| // make sure they're defined to be exported |
| typedef void (*QtMsgHandler)(QtMsgType, const char *); |
| Q_CORE_EXPORT QtMsgHandler qInstallMsgHandler(QtMsgHandler); |
| #endif |
| |
| static void qDefaultMsgHandler(QtMsgType type, const char *buf); |
| static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &buf); |
| |
| // pointer to QtMsgHandler debug handler (without context) |
| static QBasicAtomicPointer<void (QtMsgType, const char*)> msgHandler = Q_BASIC_ATOMIC_INITIALIZER(nullptr); |
| // pointer to QtMessageHandler debug handler (with context) |
| 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 |
| #else |
| |
| #if QT_CONFIG(slog2) |
| #ifndef QT_LOG_CODE |
| #define QT_LOG_CODE 9000 |
| #endif |
| |
| static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) |
| { |
| if (shouldLogToStderr()) |
| return false; // Leave logging up to stderr handler |
| |
| QString formattedMessage = qFormatLogMessage(type, context, message); |
| formattedMessage.append(QLatin1Char('\n')); |
| if (slog2_set_default_buffer((slog2_buffer_t)-1) == 0) { |
| slog2_buffer_set_config_t buffer_config; |
| slog2_buffer_t buffer_handle; |
| |
| buffer_config.buffer_set_name = __progname; |
| buffer_config.num_buffers = 1; |
| buffer_config.verbosity_level = SLOG2_DEBUG1; |
| buffer_config.buffer_config[0].buffer_name = "default"; |
| buffer_config.buffer_config[0].num_pages = 8; |
| |
| if (slog2_register(&buffer_config, &buffer_handle, 0) == -1) { |
| fprintf(stderr, "Error registering slogger2 buffer!\n"); |
| fprintf(stderr, "%s", formattedMessage.toLocal8Bit().constData()); |
| fflush(stderr); |
| return false; |
| } |
| |
| // Set as the default buffer |
| slog2_set_default_buffer(buffer_handle); |
| } |
| int severity; |
| //Determines the severity level |
| switch (type) { |
| case QtDebugMsg: |
| severity = SLOG2_DEBUG1; |
| break; |
| case QtInfoMsg: |
| severity = SLOG2_INFO; |
| break; |
| case QtWarningMsg: |
| severity = SLOG2_NOTICE; |
| break; |
| case QtCriticalMsg: |
| severity = SLOG2_WARNING; |
| break; |
| case QtFatalMsg: |
| severity = SLOG2_ERROR; |
| break; |
| } |
| //writes to the slog2 buffer |
| slog2c(NULL, QT_LOG_CODE, severity, formattedMessage.toLocal8Bit().constData()); |
| |
| return true; // Prevent further output to stderr |
| } |
| #endif // slog2 |
| |
| #if QT_CONFIG(journald) |
| static bool systemd_default_message_handler(QtMsgType type, |
| const QMessageLogContext &context, |
| const QString &message) |
| { |
| if (shouldLogToStderr()) |
| return false; // Leave logging up to stderr handler |
| |
| QString formattedMessage = qFormatLogMessage(type, context, message); |
| |
| int priority = LOG_INFO; // Informational |
| switch (type) { |
| case QtDebugMsg: |
| priority = LOG_DEBUG; // Debug-level messages |
| break; |
| case QtInfoMsg: |
| priority = LOG_INFO; // Informational conditions |
| break; |
| case QtWarningMsg: |
| priority = LOG_WARNING; // Warning conditions |
| break; |
| case QtCriticalMsg: |
| priority = LOG_CRIT; // Critical conditions |
| break; |
| case QtFatalMsg: |
| priority = LOG_ALERT; // Action must be taken immediately |
| break; |
| } |
| |
| sd_journal_send("MESSAGE=%s", formattedMessage.toUtf8().constData(), |
| "PRIORITY=%i", priority, |
| "CODE_FUNC=%s", context.function ? context.function : "unknown", |
| "CODE_LINE=%d", context.line, |
| "CODE_FILE=%s", context.file ? context.file : "unknown", |
| "QT_CATEGORY=%s", context.category ? context.category : "unknown", |
| NULL); |
| |
| return true; // Prevent further output to stderr |
| } |
| #endif |
| |
| #if QT_CONFIG(syslog) |
| static bool syslog_default_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) |
| { |
| if (shouldLogToStderr()) |
| return false; // Leave logging up to stderr handler |
| |
| QString formattedMessage = qFormatLogMessage(type, context, message); |
| |
| int priority = LOG_INFO; // Informational |
| switch (type) { |
| case QtDebugMsg: |
| priority = LOG_DEBUG; // Debug-level messages |
| break; |
| case QtInfoMsg: |
| priority = LOG_INFO; // Informational conditions |
| break; |
| case QtWarningMsg: |
| priority = LOG_WARNING; // Warning conditions |
| break; |
| case QtCriticalMsg: |
| priority = LOG_CRIT; // Critical conditions |
| break; |
| case QtFatalMsg: |
| priority = LOG_ALERT; // Action must be taken immediately |
| break; |
| } |
| |
| syslog(priority, "%s", formattedMessage.toUtf8().constData()); |
| |
| return true; // Prevent further output to stderr |
| } |
| #endif |
| |
| #if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) |
| static bool android_default_message_handler(QtMsgType type, |
| const QMessageLogContext &context, |
| const QString &message) |
| { |
| if (shouldLogToStderr()) |
| return false; // Leave logging up to stderr handler |
| |
| QString formattedMessage = qFormatLogMessage(type, context, message); |
| |
| android_LogPriority priority = ANDROID_LOG_DEBUG; |
| switch (type) { |
| case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break; |
| case QtInfoMsg: priority = ANDROID_LOG_INFO; break; |
| case QtWarningMsg: priority = ANDROID_LOG_WARN; break; |
| case QtCriticalMsg: priority = ANDROID_LOG_ERROR; break; |
| case QtFatalMsg: priority = ANDROID_LOG_FATAL; break; |
| }; |
| |
| __android_log_print(priority, qPrintable(QCoreApplication::applicationName()), "%s\n", qPrintable(formattedMessage)); |
| |
| return true; // Prevent further output to stderr |
| } |
| #endif //Q_OS_ANDROID |
| |
| #ifdef Q_OS_WIN |
| static bool win_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) |
| { |
| if (shouldLogToStderr()) |
| return false; // Leave logging up to stderr handler |
| |
| QString formattedMessage = qFormatLogMessage(type, context, message); |
| formattedMessage.append(QLatin1Char('\n')); |
| OutputDebugString(reinterpret_cast<const wchar_t *>(formattedMessage.utf16())); |
| |
| return true; // Prevent further output to stderr |
| } |
| #endif |
| |
| #ifdef Q_OS_WASM |
| static bool wasm_default_message_handler(QtMsgType type, |
| const QMessageLogContext &context, |
| const QString &message) |
| { |
| if (shouldLogToStderr()) |
| return false; // Leave logging up to stderr handler |
| |
| QString formattedMessage = qFormatLogMessage(type, context, message); |
| int emOutputFlags = (EM_LOG_CONSOLE | EM_LOG_DEMANGLE); |
| QByteArray localMsg = message.toLocal8Bit(); |
| switch (type) { |
| case QtDebugMsg: |
| break; |
| case QtInfoMsg: |
| break; |
| case QtWarningMsg: |
| emOutputFlags |= EM_LOG_WARN; |
| break; |
| case QtCriticalMsg: |
| emOutputFlags |= EM_LOG_ERROR; |
| break; |
| case QtFatalMsg: |
| emOutputFlags |= EM_LOG_ERROR; |
| } |
| emscripten_log(emOutputFlags, "%s\n", qPrintable(formattedMessage)); |
| |
| return true; // Prevent further output to stderr |
| } |
| #endif |
| |
| #endif // Bootstrap check |
| |
| // -------------------------------------------------------------------------- |
| |
| static void stderr_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) |
| { |
| QString formattedMessage = qFormatLogMessage(type, context, message); |
| |
| // 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); |
| } |
| |
| /*! |
| \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 (!handledStderr) |
| stderr_message_handler(type, context, message); |
| } |
| |
| /*! |
| \internal |
| */ |
| static void qDefaultMsgHandler(QtMsgType type, const char *buf) |
| { |
| QMessageLogContext emptyContext; |
| qDefaultMessageHandler(type, emptyContext, QString::fromLocal8Bit(buf)); |
| } |
| |
| #if defined(Q_COMPILER_THREAD_LOCAL) |
| |
| static thread_local bool msgHandlerGrabbed = false; |
| |
| static bool grabMessageHandler() |
| { |
| if (msgHandlerGrabbed) |
| return false; |
| |
| msgHandlerGrabbed = true; |
| return true; |
| } |
| |
| static void ungrabMessageHandler() |
| { |
| msgHandlerGrabbed = false; |
| } |
| |
| #else |
| static bool grabMessageHandler() { return true; } |
| static void ungrabMessageHandler() { } |
| #endif // (Q_COMPILER_THREAD_LOCAL) |
| |
| static void qt_message_print(QtMsgType msgType, const QMessageLogContext &context, const QString &message) |
| { |
| #ifndef QT_BOOTSTRAPPED |
| Q_TRACE(qt_message_print, msgType, context.category, context.function, context.file, context.line, message); |
| |
| // qDebug, qWarning, ... macros do not check whether category is enabledgc |
| if (msgType != QtFatalMsg && isDefaultCategory(context.category)) { |
| if (QLoggingCategory *defaultCategory = QLoggingCategory::defaultCategory()) { |
| if (!defaultCategory->isEnabled(msgType)) |
| return; |
| } |
| } |
| #endif |
| |
| // prevent recursion in case the message handler generates messages |
| // itself, e.g. by using Qt API |
| if (grabMessageHandler()) { |
| const auto ungrab = qScopeGuard([]{ ungrabMessageHandler(); }); |
| auto oldStyle = msgHandler.loadAcquire(); |
| auto newStye = messageHandler.loadAcquire(); |
| // prefer new message handler over the old one |
| if (newStye || !oldStyle) { |
| (newStye ? newStye : qDefaultMessageHandler)(msgType, context, message); |
| } else { |
| (oldStyle ? oldStyle : qDefaultMsgHandler)(msgType, message.toLocal8Bit().constData()); |
| } |
| } else { |
| fprintf(stderr, "%s\n", message.toLocal8Bit().constData()); |
| } |
| } |
| |
| static void qt_message_print(const QString &message) |
| { |
| #if defined(Q_OS_WINRT) |
| OutputDebugString(reinterpret_cast<const wchar_t*>(message.utf16())); |
| return; |
| #elif defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED) |
| if (!shouldLogToStderr()) { |
| OutputDebugString(reinterpret_cast<const wchar_t*>(message.utf16())); |
| return; |
| } |
| #endif |
| fprintf(stderr, "%s", message.toLocal8Bit().constData()); |
| fflush(stderr); |
| } |
| |
| static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, const QString &message) |
| { |
| #if defined(Q_CC_MSVC) && 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 |
| // wouldn't be able to change it later on... |
| convert_to_wchar_t_elided(contextFileL, sizeof contextFileL / sizeof *contextFileL, |
| context.file); |
| // get the current report mode |
| int reportMode = _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW); |
| _CrtSetReportMode(_CRT_ERROR, reportMode); |
| |
| int ret = _CrtDbgReportW(_CRT_ERROR, contextFileL, context.line, _CRT_WIDE(QT_VERSION_STR), |
| reinterpret_cast<const wchar_t *>(message.utf16())); |
| if ((ret == 0) && (reportMode & _CRTDBG_MODE_WNDW)) |
| return; // ignore |
| else if (ret == 1) |
| _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 |
| } |
| |
| |
| /*! |
| \internal |
| */ |
| void qt_message_output(QtMsgType msgType, const QMessageLogContext &context, const QString &message) |
| { |
| qt_message_print(msgType, context, message); |
| if (isFatal(msgType)) |
| qt_message_fatal(msgType, context, message); |
| } |
| |
| void qErrnoWarning(const char *msg, ...) |
| { |
| // qt_error_string() will allocate anyway, so we don't have |
| // to be careful here (like we do in plain qWarning()) |
| QString error_string = qt_error_string(-1); // before vasprintf changes errno/GetLastError() |
| |
| va_list ap; |
| va_start(ap, msg); |
| QString buf = QString::vasprintf(msg, ap); |
| va_end(ap); |
| |
| buf += QLatin1String(" (") + error_string + QLatin1Char(')'); |
| QMessageLogContext context; |
| qt_message_output(QtCriticalMsg, context, buf); |
| } |
| |
| void qErrnoWarning(int code, const char *msg, ...) |
| { |
| // qt_error_string() will allocate anyway, so we don't have |
| // to be careful here (like we do in plain qWarning()) |
| va_list ap; |
| va_start(ap, 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); |
| } |
| |
| /*! |
| \typedef QtMsgHandler |
| \relates <QtGlobal> |
| \deprecated |
| |
| This is a typedef for a pointer to a function with the following |
| signature: |
| |
| \snippet code/src_corelib_global_qglobal.cpp 7 |
| |
| This typedef is deprecated, you should use QtMessageHandler instead. |
| \sa QtMsgType, QtMessageHandler, qInstallMsgHandler(), qInstallMessageHandler() |
| */ |
| |
| /*! |
| \typedef QtMessageHandler |
| \relates <QtGlobal> |
| \since 5.0 |
| |
| This is a typedef for a pointer to a function with the following |
| signature: |
| |
| \snippet code/src_corelib_global_qglobal.cpp 49 |
| |
| \sa QtMsgType, qInstallMessageHandler() |
| */ |
| |
| /*! |
| \fn QtMessageHandler qInstallMessageHandler(QtMessageHandler handler) |
| \relates <QtGlobal> |
| \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: |
| |
| \snippet code/src_corelib_global_qglobal.cpp 23 |
| |
| \sa QtMessageHandler, QtMsgType, qDebug(), qInfo(), qWarning(), qCritical(), qFatal(), |
| {Debugging Techniques} |
| */ |
| |
| /*! |
| \fn QtMsgHandler qInstallMsgHandler(QtMsgHandler handler) |
| \relates <QtGlobal> |
| \deprecated |
| |
| Installs a Qt message \a handler which has been defined |
| previously. This method is deprecated, use qInstallMessageHandler |
| instead. |
| \sa QtMsgHandler, qInstallMessageHandler() |
| */ |
| /*! |
| \fn void qSetMessagePattern(const QString &pattern) |
| \relates <QtGlobal> |
| \since 5.0 |
| |
| \brief Changes the output of the default message handler. |
| |
| Allows to tweak the output of qDebug(), qInfo(), qWarning(), qCritical(), |
| and qFatal(). The category logging output of qCDebug(), qCInfo(), |
| qCWarning(), and qCCritical() is formatted, too. |
| |
| Following placeholders are supported: |
| |
| \table |
| \header \li Placeholder \li Description |
| \row \li \c %{appname} \li QCoreApplication::applicationName() |
| \row \li \c %{category} \li Logging category |
| \row \li \c %{file} \li Path to source file |
| \row \li \c %{function} \li Function |
| \row \li \c %{line} \li Line in source file |
| \row \li \c %{message} \li The actual message |
| \row \li \c %{pid} \li QCoreApplication::applicationPid() |
| \row \li \c %{threadid} \li The system-wide ID of current thread (if it can be obtained) |
| \row \li \c %{qthreadptr} \li A pointer to the current QThread (result of QThread::currentThread()) |
| \row \li \c %{type} \li "debug", "warning", "critical" or "fatal" |
| \row \li \c %{time process} \li time of the message, in seconds since the process started (the token "process" is literal) |
| \row \li \c %{time boot} \li the time of the message, in seconds since the system boot if that |
| can be determined (the token "boot" is literal). If the time since boot could not be obtained, |
| the output is indeterminate (see QElapsedTimer::msecsSinceReference()). |
| \row \li \c %{time [format]} \li system time when the message occurred, formatted by |
| passing the \c format to \l QDateTime::toString(). If the format is |
| not specified, the format of Qt::ISODate is used. |
| \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}. |
| When reading backtraces, take into account that frames might be missing due to inlining or |
| tail call optimization. |
| \endtable |
| |
| You can also use conditionals on the type of the message using \c %{if-debug}, \c %{if-info} |
| \c %{if-warning}, \c %{if-critical} or \c %{if-fatal} followed by an \c %{endif}. |
| What is inside the \c %{if-*} and \c %{endif} will only be printed if the type matches. |
| |
| Finally, text inside \c %{if-category} ... \c %{endif} is only printed if the category |
| is not the default one. |
| |
| Example: |
| \snippet code/src_corelib_global_qlogging.cpp 0 |
| |
| The default \a pattern is "%{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 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} |
| */ |
| |
| QtMessageHandler qInstallMessageHandler(QtMessageHandler h) |
| { |
| const auto old = messageHandler.fetchAndStoreOrdered(h); |
| if (old) |
| return old; |
| else |
| return qDefaultMessageHandler; |
| } |
| |
| QtMsgHandler qInstallMsgHandler(QtMsgHandler h) |
| { |
| const auto old = msgHandler.fetchAndStoreOrdered(h); |
| if (old) |
| return old; |
| else |
| return qDefaultMsgHandler; |
| } |
| |
| void qSetMessagePattern(const QString &pattern) |
| { |
| const auto locker = qt_scoped_lock(QMessagePattern::mutex); |
| |
| if (!qMessagePattern()->fromEnvironment) |
| qMessagePattern()->setPattern(pattern); |
| } |
| |
| |
| /*! |
| Copies context information from \a logContext into this QMessageLogContext. |
| Returns a reference to this object. |
| |
| Note that the version is \b not copied, only the context information. |
| |
| \internal |
| */ |
| QMessageLogContext &QMessageLogContext::copyContextFrom(const QMessageLogContext &logContext) noexcept |
| { |
| this->category = logContext.category; |
| this->file = logContext.file; |
| this->line = logContext.line; |
| this->function = logContext.function; |
| return *this; |
| } |
| |
| /*! |
| \fn QMessageLogger::QMessageLogger() |
| |
| Constructs a default QMessageLogger. See the other constructors to specify |
| context information. |
| */ |
| |
| /*! |
| \fn QMessageLogger::QMessageLogger(const char *file, int line, const char *function) |
| |
| Constructs a QMessageLogger to record log messages for \a file at \a line |
| in \a function. The is equivalent to QMessageLogger(file, line, function, "default") |
| */ |
| /*! |
| \fn QMessageLogger::QMessageLogger(const char *file, int line, const char *function, const char *category) |
| |
| Constructs a QMessageLogger to record \a category messages for \a file at \a line |
| in \a function. |
| */ |
| |
| /*! |
| \fn void QMessageLogger::noDebug(const char *, ...) const |
| \internal |
| |
| Ignores logging output |
| |
| \sa QNoDebug, qDebug() |
| */ |
| |
| /*! |
| \fn QMessageLogContext::QMessageLogContext() |
| \internal |
| |
| Constructs a QMessageLogContext |
| */ |
| |
| /*! |
| \fn QMessageLogContext::QMessageLogContext(const char *fileName, int lineNumber, const char *functionName, const char *categoryName) |
| \internal |
| |
| Constructs a QMessageLogContext with for file \a fileName at line |
| \a lineNumber, in function \a functionName, and category \a categoryName. |
| */ |
| |
| QT_END_NAMESPACE |