logging: Clarify and document how we look for the presence of a console

The privately exported qt_logging_to_console() function has been renamed
to shouldLogToStderr, and exported in the QtPrivate namespace for QtTestLib.

[ChangeLog][Important behavior changes][Logging (including qDebug and
qWarning)] The QT_LOGGING_TO_CONSOLE environment variable has been
deprecated. Use the more specific QT_ASSUME_STDERR_HAS_CONSOLE or
QT_FORCE_STDERR_LOGGING, depending on your usecase.

Change-Id: Ie29228eeac3b700c3de94fee022d5d66d9b5c210
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Tor Arne Vestbø 2018-02-08 12:51:27 +01:00
parent 3d53cf976d
commit 67d5f79fe6
4 changed files with 169 additions and 60 deletions

View File

@ -909,7 +909,7 @@ Mozilla License) is included. The data is then also used in QNetworkCookieJar::v
"condition": "features.journald || features.syslog || (config.qnx && features.slog2)",
"message": "journald, syslog or slog2 integration is enabled.
If your users intend to develop applications against this build,
ensure that the IDEs they use either set QT_LOGGING_TO_CONSOLE to 1
ensure that the IDEs they use either set QT_FORCE_STDERR_LOGGING to 1
or are able to read the logged output from journald, syslog or slog2."
},
{

View File

@ -41,6 +41,7 @@
#include "qglobal_p.h"
#include "qlogging.h"
#include "qlogging_p.h"
#include "qlist.h"
#include "qbytearray.h"
#include "qstring.h"
@ -204,56 +205,102 @@ static bool isDefaultCategory(const char *category)
return !category || strcmp(category, "default") == 0;
}
static bool willLogToConsole()
/*!
Returns true if writing to \c stderr is supported.
\sa stderrHasConsoleAttached()
*/
static bool systemHasStderr()
{
#if defined(Q_OS_WINRT)
// these systems have no stderr, so always log to the system log
return false;
#else
// rules to determine if we'll log preferably to the console:
// 1) if QT_LOGGING_TO_CONSOLE is set, it determines behavior:
// - if it's set to 0, we will not log to console
// - if it's set to 1, we will log to console
// 2) otherwise, we will log to console if we have a console window (Windows)
// or a controlling TTY (Unix). This is done even if stderr was redirected
// to the blackhole device (NUL or /dev/null).
bool ok = true;
uint envcontrol = qgetenv("QT_LOGGING_TO_CONSOLE").toUInt(&ok);
if (ok)
return envcontrol;
# 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(_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);
} else if (devtty != -1) {
// there is a /dev/tty and we could open it: we have a controlling TTY
qt_safe_close(devtty);
return true;
}
// no controlling TTY
return false;
# else
# error "Not Unix and not Windows?"
# endif
return false; // WinRT has no stderr
#endif
return true;
}
Q_CORE_EXPORT bool qt_logging_to_console()
/*!
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 can not 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().
\sa systemHasStderr()
*/
bool stderrHasConsoleAttached()
{
static const bool logToConsole = willLogToConsole();
return logToConsole;
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.
\sa stderrHasConsoleAttached()
*/
bool shouldLogToStderr()
{
static bool forceStderrLogging = qEnvironmentVariableIntValue("QT_FORCE_STDERR_LOGGING");
return forceStderrLogging || stderrHasConsoleAttached();
}
} // QtPrivate
using namespace QtPrivate;
/*!
\class QMessageLogContext
\inmodule QtCore
@ -1472,8 +1519,8 @@ static QBasicAtomicPointer<void (QtMsgType, const QMessageLogContext &, const QS
static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
if (qt_logging_to_console())
return false;
if (shouldLogToStderr())
return false; // Leave logging up to stderr handler
QString formattedMessage = qFormatLogMessage(type, context, message);
formattedMessage.append(QLatin1Char('\n'));
@ -1528,8 +1575,8 @@ static bool systemd_default_message_handler(QtMsgType type,
const QMessageLogContext &context,
const QString &message)
{
if (qt_logging_to_console())
return false;
if (shouldLogToStderr())
return false; // Leave logging up to stderr handler
QString formattedMessage = qFormatLogMessage(type, context, message);
@ -1567,8 +1614,8 @@ static bool systemd_default_message_handler(QtMsgType type,
#if QT_CONFIG(syslog)
static bool syslog_default_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
if (qt_logging_to_console())
return false;
if (shouldLogToStderr())
return false; // Leave logging up to stderr handler
QString formattedMessage = qFormatLogMessage(type, context, message);
@ -1602,8 +1649,8 @@ static bool android_default_message_handler(QtMsgType type,
const QMessageLogContext &context,
const QString &message)
{
if (qt_logging_to_console())
return false;
if (shouldLogToStderr())
return false; // Leave logging up to stderr handler
QString formattedMessage = qFormatLogMessage(type, context, message);
@ -1627,8 +1674,8 @@ static bool android_default_message_handler(QtMsgType type,
#ifdef Q_OS_WIN
static bool win_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
if (qt_logging_to_console())
return false;
if (shouldLogToStderr())
return false; // Leave logging up to stderr handler
QString formattedMessage = qFormatLogMessage(type, context, message);
formattedMessage.append(QLatin1Char('\n'));
@ -1756,7 +1803,7 @@ static void qt_message_print(const QString &message)
OutputDebugString(reinterpret_cast<const wchar_t*>(message.utf16()));
return;
#elif defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED)
if (!qt_logging_to_console()) {
if (!shouldLogToStderr()) {
OutputDebugString(reinterpret_cast<const wchar_t*>(message.utf16()));
return;
}

View File

@ -0,0 +1,64 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** 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$
**
****************************************************************************/
#ifndef QLOGGING_P_H
#define QLOGGING_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of a number of Qt sources files. This header file may change from
// version to version without notice, or even be removed.
//
// We mean it.
//
QT_BEGIN_NAMESPACE
namespace QtPrivate {
Q_CORE_EXPORT bool shouldLogToStderr();
}
QT_END_NAMESPACE
#endif // QLOGGING_P_H

View File

@ -44,6 +44,8 @@
#include <QtTest/private/qbenchmark_p.h>
#include <QtTest/private/qbenchmarkmetric_p.h>
#include <QtCore/private/qlogging_p.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@ -205,15 +207,11 @@ namespace QTest {
}
}
#if defined(Q_OS_WIN)
Q_CORE_EXPORT bool qt_logging_to_console(); // defined in qlogging.cpp
#endif
void QPlainTestLogger::outputMessage(const char *str)
{
#if defined(Q_OS_WIN)
// log to system log only if output is not redirected, and no console is attached
if (!qt_logging_to_console() && stream == stdout) {
// Log to system log only if output is not redirected and stderr not preferred
if (stream == stdout && !QtPrivate::shouldLogToStderr()) {
OutputDebugStringA(str);
return;
}