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.cpp324
1 files changed, 215 insertions, 109 deletions
diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp
index f99675a7b9..9f1fe7cabb 100644
--- a/src/corelib/global/qlogging.cpp
+++ b/src/corelib/global/qlogging.cpp
@@ -63,11 +63,18 @@
#if QT_CONFIG(slog2)
#include <sys/slog2.h>
#endif
+#if QT_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>
@@ -152,6 +159,21 @@ 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)
{
@@ -159,25 +181,34 @@ static bool isFatal(QtMsgType msgType)
return true;
if (msgType == QtCriticalMsg) {
- static bool fatalCriticals = !qEnvironmentVariableIsEmpty("QT_FATAL_CRITICALS");
- return fatalCriticals;
+ 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.load() && fatalCriticals.fetchAndAddRelaxed(-1) == 1;
}
if (msgType == QtWarningMsg || msgType == QtCriticalMsg) {
- static bool fatalWarnings = !qEnvironmentVariableIsEmpty("QT_FATAL_WARNINGS");
- return fatalWarnings;
+ 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.load() && fatalWarnings.fetchAndAddRelaxed(-1) == 1;
}
return false;
}
+static bool isDefaultCategory(const char *category)
+{
+ return !category || strcmp(category, "default") == 0;
+}
+
static bool willLogToConsole()
{
#if defined(Q_OS_WINRT)
// these systems have no stderr, so always log to the system log
return false;
-#elif defined(QT_BOOTSTRAPPED)
- return true;
#else
// rules to determine if we'll log preferably to the console:
// 1) if QT_LOGGING_TO_CONSOLE is set, it determines behavior:
@@ -195,8 +226,11 @@ static bool willLogToConsole()
# ifdef Q_OS_WIN
return GetConsoleWindow();
# elif defined(Q_OS_UNIX)
+# ifndef _PATH_TTY
+# define _PATH_TTY "/dev/tty"
+# endif
// if /dev/tty exists, we can only open it if we have a controlling TTY
- int devtty = qt_safe_open("/dev/tty", O_RDONLY);
+ int devtty = qt_safe_open(_PATH_TTY, O_RDONLY);
if (devtty == -1 && (errno == ENOENT || errno == EPERM || errno == ENXIO)) {
// no /dev/tty, fall back to isatty on stderr
return isatty(STDERR_FILENO);
@@ -1183,20 +1217,10 @@ void QMessagePattern::setPattern(const QString &pattern)
error += QLatin1String("QT_MESSAGE_PATTERN: %{if-*} cannot be nested\n");
else if (inIf)
error += QLatin1String("QT_MESSAGE_PATTERN: missing %{endif}\n");
- if (!error.isEmpty()) {
-#if defined(Q_OS_WINRT)
- OutputDebugString(reinterpret_cast<const wchar_t*>(error.utf16()));
- if (0)
-#elif defined(Q_OS_WIN) && defined(QT_BUILD_CORE_LIB)
- if (!qt_logging_to_console()) {
- OutputDebugString(reinterpret_cast<const wchar_t*>(error.utf16()));
- } else
-#endif
- {
- fprintf(stderr, "%s", error.toLocal8Bit().constData());
- fflush(stderr);
- }
- }
+
+ if (!error.isEmpty())
+ qt_message_print(error);
+
literals = new const char*[literalsVar.size() + 1];
literals[literalsVar.size()] = 0;
memcpy(literals, literalsVar.constData(), literalsVar.size() * sizeof(const char*));
@@ -1224,7 +1248,7 @@ static QStringList backtraceFramesForLogMessage(int frameCount)
static QRegularExpression rx(QStringLiteral("^(?:[^(]*/)?([^(/]+)\\(([^+]*)(?:[\\+[a-f0-9x]*)?\\) \\[[a-f0-9x]*\\]$"),
QRegularExpression::OptimizeOnFirstUsageOption);
- QVarLengthArray<void*, 32> buffer(7 + frameCount);
+ QVarLengthArray<void*, 32> buffer(8 + frameCount);
int n = backtrace(buffer.data(), buffer.size());
if (n > 0) {
int numberPrinted = 0;
@@ -1286,57 +1310,6 @@ static QString formatBacktraceForLogMessage(const QMessagePattern::BacktracePara
}
#endif // QLOGGING_HAVE_BACKTRACE && !QT_BOOTSTRAPPED
-#if QT_CONFIG(slog2)
-#ifndef QT_LOG_CODE
-#define QT_LOG_CODE 9000
-#endif
-
-static void slog2_default_handler(QtMsgType msgType, const char *message)
-{
- 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", message);
- fflush(stderr);
- return;
- }
-
- // Set as the default buffer
- slog2_set_default_buffer(buffer_handle);
- }
- int severity;
- //Determines the severity level
- switch (msgType) {
- 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, message);
-}
-#endif // slog2
-
Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern)
/*!
@@ -1454,7 +1427,7 @@ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, con
}
#endif // !QT_BOOTSTRAPPED
} else if (token == ifCategoryTokenC) {
- if (!context.category || (strcmp(context.category, "default") == 0))
+ if (isDefaultCategory(context.category))
skip = true;
#define HANDLE_IF_TOKEN(LEVEL) \
} else if (token == if##LEVEL##TokenC) { \
@@ -1486,11 +1459,80 @@ static QBasicAtomicPointer<void (QtMsgType, const char*)> msgHandler = Q_BASIC_A
// pointer to QtMessageHandler debug handler (with context)
static QBasicAtomicPointer<void (QtMsgType, const QMessageLogContext &, const QString &)> messageHandler = Q_BASIC_ATOMIC_INITIALIZER(qDefaultMessageHandler);
+// ------------------------ 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 (qt_logging_to_console())
+ return false;
+
+ 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 void systemd_default_message_handler(QtMsgType type,
+static bool systemd_default_message_handler(QtMsgType type,
const QMessageLogContext &context,
const QString &message)
{
+ if (qt_logging_to_console())
+ return false;
+
+ QString formattedMessage = qFormatLogMessage(type, context, message);
+
int priority = LOG_INFO; // Informational
switch (type) {
case QtDebugMsg:
@@ -1510,19 +1552,26 @@ static void systemd_default_message_handler(QtMsgType type,
break;
}
- sd_journal_send("MESSAGE=%s", message.toUtf8().constData(),
+ 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 void syslog_default_message_handler(QtMsgType type, const char *message)
+static bool syslog_default_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
+ if (qt_logging_to_console())
+ return false;
+
+ QString formattedMessage = qFormatLogMessage(type, context, message);
+
int priority = LOG_INFO; // Informational
switch (type) {
case QtDebugMsg:
@@ -1542,15 +1591,22 @@ static void syslog_default_message_handler(QtMsgType type, const char *message)
break;
}
- syslog(priority, "%s", message);
+ 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 void android_default_message_handler(QtMsgType type,
+static bool android_default_message_handler(QtMsgType type,
const QMessageLogContext &context,
const QString &message)
{
+ if (qt_logging_to_console())
+ return false;
+
+ QString formattedMessage = qFormatLogMessage(type, context, message);
+
android_LogPriority priority = ANDROID_LOG_DEBUG;
switch (type) {
case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break;
@@ -1562,50 +1618,81 @@ static void android_default_message_handler(QtMsgType type,
__android_log_print(priority, qPrintable(QCoreApplication::applicationName()),
"%s:%d (%s): %s\n", context.file, context.line,
- context.function, qPrintable(message));
+ context.function, qPrintable(formattedMessage));
+
+ return true; // Prevent further output to stderr
}
#endif //Q_OS_ANDROID
-/*!
- \internal
-*/
-static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context,
- const QString &buf)
+#ifdef Q_OS_WIN
+static bool win_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
+{
+ if (qt_logging_to_console())
+ return false;
+
+ 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
+
+#endif // Bootstrap check
+
+// --------------------------------------------------------------------------
+
+static void stderr_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
- QString logMessage = qFormatLogMessage(type, context, buf);
+ 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 (logMessage.isNull())
+ if (formattedMessage.isNull())
return;
- if (!qt_logging_to_console()) {
-#if defined(Q_OS_WIN)
- logMessage.append(QLatin1Char('\n'));
- OutputDebugString(reinterpret_cast<const wchar_t *>(logMessage.utf16()));
- return;
-#elif QT_CONFIG(slog2)
- logMessage.append(QLatin1Char('\n'));
- slog2_default_handler(type, logMessage.toLocal8Bit().constData());
- return;
-#elif QT_CONFIG(journald)
- systemd_default_message_handler(type, context, logMessage);
- return;
-#elif QT_CONFIG(syslog)
- syslog_default_message_handler(type, logMessage.toUtf8().constData());
- return;
-#elif defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
- android_default_message_handler(type, context, logMessage);
- return;
-#endif
- }
- fprintf(stderr, "%s\n", logMessage.toLocal8Bit().constData());
+ 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)
+ if (__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *))
+ handledStderr |= AppleUnifiedLogger::messageHandler(type, context, message);
+# endif
+#endif
+
+ if (!handledStderr)
+ stderr_message_handler(type, context, message);
+}
+
+/*!
+ \internal
+*/
static void qDefaultMsgHandler(QtMsgType type, const char *buf)
{
QMessageLogContext emptyContext;
@@ -1639,7 +1726,7 @@ static void qt_message_print(QtMsgType msgType, const QMessageLogContext &contex
{
#ifndef QT_BOOTSTRAPPED
// qDebug, qWarning, ... macros do not check whether category is enabled
- if (!context.category || (strcmp(context.category, "default") == 0)) {
+ if (isDefaultCategory(context.category)) {
if (QLoggingCategory *defaultCategory = QLoggingCategory::defaultCategory()) {
if (!defaultCategory->isEnabled(msgType))
return;
@@ -1663,6 +1750,21 @@ static void qt_message_print(QtMsgType msgType, const QMessageLogContext &contex
}
}
+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 (!qt_logging_to_console()) {
+ 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)
@@ -1860,6 +1962,10 @@ void qErrnoWarning(int code, const char *msg, ...)
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}