From 3efca77e35c0c336961f5e9640382ef87e83e794 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Thu, 10 Jan 2013 16:15:05 +0100 Subject: [PATCH] Import qlogger framework Merge most parts of the qlogger framework from git://gitorious.org/qtplayground/qlogger.git The categorized logging feature is a replacement for qDebug, qWarning and friends. With logging statements in an app/library, a developer can turn on the statements they care about and turn off the ones they don't. Most work for this was done by Wolfgang Beck and Lincoln Ramsay. Task-number: QTBUG-25694 Change-Id: Ib0cdfbbf3694f86ad9ec553b2ea36f09a477cded Reviewed-by: Thiago Macieira --- .../doc/snippets/qloggingcategory/main.cpp | 113 +++ src/corelib/global/qlogging.cpp | 11 + src/corelib/io/io.pri | 9 +- src/corelib/io/qloggingcategory.cpp | 352 ++++++++ src/corelib/io/qloggingcategory.h | 138 +++ src/corelib/io/qloggingcategory_p.h | 66 ++ src/corelib/io/qloggingregistry.cpp | 312 +++++++ src/corelib/io/qloggingregistry_p.h | 135 +++ tests/auto/corelib/io/io.pro | 1 + .../io/qloggingcategory/qloggingcategory.pro | 7 + .../qloggingcategory/tst_qloggingcategory.cpp | 818 ++++++++++++++++++ .../auto/corelib/io/qnodebug/tst_qnodebug.cpp | 5 + 12 files changed, 1965 insertions(+), 2 deletions(-) create mode 100644 src/corelib/doc/snippets/qloggingcategory/main.cpp create mode 100644 src/corelib/io/qloggingcategory.cpp create mode 100644 src/corelib/io/qloggingcategory.h create mode 100644 src/corelib/io/qloggingcategory_p.h create mode 100644 src/corelib/io/qloggingregistry.cpp create mode 100644 src/corelib/io/qloggingregistry_p.h create mode 100644 tests/auto/corelib/io/qloggingcategory/qloggingcategory.pro create mode 100644 tests/auto/corelib/io/qloggingcategory/tst_qloggingcategory.cpp diff --git a/src/corelib/doc/snippets/qloggingcategory/main.cpp b/src/corelib/doc/snippets/qloggingcategory/main.cpp new file mode 100644 index 0000000000..d6d9ae0ff3 --- /dev/null +++ b/src/corelib/doc/snippets/qloggingcategory/main.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +//![1] +// in a header +Q_DECLARE_LOGGING_CATEGORY(QT_DRIVER_USB) + +// in one source file +Q_LOGGING_CATEGORY(QT_DRIVER_USB, "qt.driver.usb") +//![1] + + +// Completely made up example, inspired by en.wikipedia.org/wiki/USB :) +struct UsbEntry { + int id; + int classtype; +}; + +QDebug operator<<(QDebug &dbg, const UsbEntry &entry) +{ + dbg.nospace() << "" << entry.id << " (" << entry.classtype << ")"; + return dbg.space(); +} + +QList usbEntries() { + QList entries; + return entries; +} + +void main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + +//![2] + // don't run the expensive code if the string won't print + if (QT_DRIVER_USB().isEnabled()) { + QStringList items; + foreach (const UsbEntry &entry, usbEntries()) + items << QString("%1 (%2)").arg(entry.id, entry.classtype); + qCDebug(QT_DRIVER_USB) << "devices: " << items; + } +//![2] + +//![3] + // usbEntries() will only be called if QT_DRIVER_USB category is enabled + qCDebug(QT_DRIVER_USB) << "devices: " << usbEntries(); +//![3] + + { +//![10] + QLoggingCategory category("qt.driver.usb"); + qCDebug(category) << "a debug message"; +//![10] + } + + { +//![11] + QLoggingCategory category("qt.driver.usb"); + qCWarning(category) << "a warning message"; +//![11] + } + + { +//![12] + QLoggingCategory category("qt.driver.usb"); + qCCritical(category) << "a critical message"; +//![12] + } +} + +//![20] +void myCategoryFilter(QLoggingCategory *); +//![20] diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp index 5aaa0716f1..1cd11ad667 100644 --- a/src/corelib/global/qlogging.cpp +++ b/src/corelib/global/qlogging.cpp @@ -49,6 +49,8 @@ #ifndef QT_BOOTSTRAPPED #include "qcoreapplication.h" #include "qthread.h" +#include "qloggingcategory.h" +#include "private/qloggingregistry_p.h" #endif #ifdef Q_OS_WIN #include @@ -920,6 +922,14 @@ static void qDefaultMsgHandler(QtMsgType type, const char *buf) static void qt_message_print(QtMsgType msgType, const QMessageLogContext &context, const QString &message) { +#ifndef QT_BOOTSTRAPPED + // qDebug, qWarning, ... macros do not check whether category is enabled + if (!context.category || (strcmp(context.category, "default") == 0)) { + if (!QLoggingCategory::defaultCategory().isEnabled(msgType)) + return; + } +#endif + if (!msgHandler) msgHandler = qDefaultMsgHandler; if (!messageHandler) @@ -1151,6 +1161,7 @@ void qSetMessagePattern(const QString &pattern) qMessagePattern()->setPattern(pattern); } + /*! Copies context information from \a logContext into this QMessageLogContext \internal diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index c00947dd97..b7fc1bc600 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -49,7 +49,10 @@ HEADERS += \ io/qfilesystemmetadata_p.h \ io/qfilesystemiterator_p.h \ io/qfileselector.h \ - io/qfileselector_p.h + io/qfileselector_p.h \ + io/qloggingcategory.h \ + io/qloggingcategory_p.h \ + io/qloggingregistry_p.h SOURCES += \ io/qabstractfileengine.cpp \ @@ -86,7 +89,9 @@ SOURCES += \ io/qfilesystemwatcher_polling.cpp \ io/qfilesystementry.cpp \ io/qfilesystemengine.cpp \ - io/qfileselector.cpp + io/qfileselector.cpp \ + io/qloggingcategory.cpp \ + io/qloggingregistry.cpp win32 { SOURCES += io/qsettings_win.cpp diff --git a/src/corelib/io/qloggingcategory.cpp b/src/corelib/io/qloggingcategory.cpp new file mode 100644 index 0000000000..562cf25964 --- /dev/null +++ b/src/corelib/io/qloggingcategory.cpp @@ -0,0 +1,352 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qloggingcategory.h" +#include "qloggingcategory_p.h" +#include "qloggingregistry_p.h" + +QT_BEGIN_NAMESPACE + +const char qtDefaultCategoryName[] = "default"; + +Q_GLOBAL_STATIC_WITH_ARGS(QLoggingCategory, qtDefaultCategory, + (qtDefaultCategoryName)) + +/*! + \class QLoggingCategory + \inmodule QtCore + \since 5.2 + + \brief A category, or 'area' in the logging infrastructure. + + QLoggingCategory represents a certain logging category - identified + by a string - at runtime. Whether a category should be actually logged or + not can be checked with the \l isEnabled() methods. + + \section1 Creating category objects + + Qt provides the \l Q_LOGGING_CATEGORY(), Q_DECLARE_LOGGING_CATEGORY() macros + to conveniently create static QLoggingCategory objects on the heap: + + \snippet qloggingcategory/main.cpp 1 + + \section1 Checking category configuration + + QLoggingCategory provides two isEnabled methods, a template one and a + non-template one, for checking whether the current category is enabled. + The template version checks for the most common case that no special rules + are applied in inline code, and should be preferred: + + \snippet qloggingcategory/main.cpp 2 + + Note that qCDebug() prevents arguments from being evaluated + if the string won't print, so calling isEnabled explicitly is not needed: + \l isEnabled(). + + \snippet qloggingcategory/main.cpp 3 + + \section1 Default configuration + + In the default configuration \l isEnabled() will return true for all + \l QtMsgType types except QtDebugMsg: QtDebugMsg is only active by default + for the \c "default" category. + + \section1 Changing configuration + + The default configuration can be changed by calling \l setEnabled(). However, + this only affects the current category object, not e.g. another object for the + same category name. Use either \l setFilterRules() or \l installFilter() to + configure categories globally. +*/ + +/*! + Constructs a QLoggingCategory object with the provided \a category name. + The object becomes the local identifier for the category. + + If \a category is \c{0}, the category name is changed to \c{"default"}. +*/ +QLoggingCategory::QLoggingCategory(const char *category) + : name(0), + enabledDebug(false), + enabledWarning(true), + enabledCritical(true) +{ + bool isDefaultCategory + = (category == 0) || (strcmp(category, qtDefaultCategoryName) == 0); + + if (isDefaultCategory) { + // normalize default category names, so that we can just do + // pointer comparison in QLoggingRegistry::updateCategory + name = qtDefaultCategoryName; + enabledDebug = true; + } else { + name = category; + } + + if (QLoggingRegistry *reg = QLoggingRegistry::instance()) + reg->registerCategory(this);} + +/*! + Destructs a QLoggingCategory object +*/ +QLoggingCategory::~QLoggingCategory() +{ + if (QLoggingRegistry *reg = QLoggingRegistry::instance()) + reg->unregisterCategory(this); +} + +/*! + \fn const char *QLoggingCategory::categoryName() const + + Returns the name of the category. +*/ + +/*! + \fn bool QLoggingCategory::isEnabled() const + + Returns true if a message of the template \c QtMsgType argument should be + shown. Returns false otherwise. + + \note The qCDebug, qCWarning, qCCritical macros already do this check before + executing any code. However, calling this method may be useful to avoid + expensive generation of data that is only used for debug output. +*/ + +/*! + Returns true if a message of type \a msgtype for the category should be + shown. Returns false otherwise. + + \note The templated, inline version of this method, \l isEnabled(), is + optimized for the common case that no configuration is set, and should + generally be preferred. +*/ +bool QLoggingCategory::isEnabled(QtMsgType msgtype) const +{ + switch (msgtype) { + case QtDebugMsg: return enabledDebug; + case QtWarningMsg: return enabledWarning; + case QtCriticalMsg: return enabledCritical; + case QtFatalMsg: return true; + default: break; + } + return false; +} + +/*! + Changes the type \a type for the category to \a enable. + + Changes only affect the current QLoggingCategory object, and won't + change e.g. the settings of another objects for the same category name. + + \note QtFatalMsg cannot be changed. It will always return true. +*/ +void QLoggingCategory::setEnabled(QtMsgType type, bool enable) +{ + switch (type) { + case QtDebugMsg: enabledDebug = enable; break; + case QtWarningMsg: enabledWarning = enable; break; + case QtCriticalMsg: enabledCritical = enable; break; + case QtFatalMsg: + default: break; + } +} + +/*! + \fn QLoggingCategory &QLoggingCategory::operator()() + + Returns the object itself. This allows both a QLoggingCategory variable, and + a factory method returning a QLoggingCategory, to be used in qCDebug(), + qCWarning(), qCCritial() macros. + */ + +/*! + Returns the category "default" that is used e.g. by qDebug(), qWarning(), + qCritical(), qFatal(). + */ +QLoggingCategory &QLoggingCategory::defaultCategory() +{ + return *qtDefaultCategory(); +} + +/*! + \typedef QLoggingCategory::CategoryFilter + + This is a typedef for a pointer to a function with the following + signature: + + \snippet qloggingcategory/main.cpp 20 + + A function with this signature can be installed with \l installFilter(). +*/ + +/*! + Installs a function \a filter that is used to determine which categories + and message types should be enabled. Returns a pointer to the previous + installed filter. + + Every QLoggingCategory object created is passed to the filter, and the + filter is free to change the respective category configuration with + \l setEnabled(). + + An alternative way of configuring the default filter is via + \l setFilterRules(). + */ +QLoggingCategory::CategoryFilter +QLoggingCategory::installFilter(QLoggingCategory::CategoryFilter filter) +{ + return QLoggingRegistry::instance()->installFilter(filter); +} + + +/*! + Configures which categories and message types should be enabled through a + a set of \a rules. + + Each line in \a rules must have the format + + \code + [.] = true|false + \endcode + + where \c is the name of the category, potentially with \c{*} as a + wildcard symbol at the start and/or the end. The optional \c must + be either \c debug, \c warning, or \c critical. + + The rules might be ignored if a custom category filter is installed with + \l installFilter(). +*/ +void QLoggingCategory::setFilterRules(const QString &rules) +{ + QLoggingRegistry::instance()->rulesParser.setRules(rules); +} + +/*! + \macro qCDebug(category) + \relates QLoggingCategory + \since 5.2 + + Returns an output stream for debug messages in the logging category + \a category. + + The macro expands to code that first checks whether + \l QLoggingCategory::isEnabled() evaluates for debug output to \c{true}. + If so, the stream arguments are processed and sent to the message handler. + + Example: + + \snippet qloggingcategory/main.cpp 10 + + \note Arguments are not processed if debug output for the category is not + enabled, so do not rely on any side effects. + + \sa qDebug() +*/ + +/*! + \macro qCWarning(category) + \relates QLoggingCategory + \since 5.2 + + Returns an output stream for warning messages in the logging category + \a category. + + The macro expands to code that first checks whether + \l QLoggingCategory::isEnabled() evaluates for warning output to \c{true}. + If so, the stream arguments are processed and sent to the message handler. + + Example: + + \snippet qloggingcategory/main.cpp 11 + + \note Arguments are not processed if warning output for the category is not + enabled, so do not rely on any side effects. + + \sa qWarning() +*/ + +/*! + \macro qCCritical(category) + \relates QLoggingCategory + \since 5.2 + + Returns an output stream for critical messages in the logging category + \a category. + + The macro expands to code that first checks whether + \l QLoggingCategory::isEnabled() evaluates for critical output to \c{true}. + If so, the stream arguments are processed and sent to the message handler. + + Example: + + \snippet qloggingcategory/main.cpp 12 + + \note Arguments are not processed if critical output for the category is not + enabled, so do not reply on any side effects. + + \sa qCritical() +*/ + +/*! + \macro Q_DECLARE_LOGGING_CATEGORY(name) + \relates QLoggingCategory + \since 5.2 + + Declares a logging category \a name. The macro can be used to declare + a common logging category shared in different parts of the program. + + This macro must be used outside of a class or method. +*/ + +/*! + \macro Q_LOGGING_CATEGORY(name, string) + \relates QLoggingCategory + \since 5.2 + + Defines a logging category \a name, and makes it configurable under the + \a string identifier. + + Only one translation unit in a library or executable can define a category + with a specific name. + + This macro must be used outside of a class or method. +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/io/qloggingcategory.h b/src/corelib/io/qloggingcategory.h new file mode 100644 index 0000000000..90111c96fa --- /dev/null +++ b/src/corelib/io/qloggingcategory.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLOGGINGCATEGORY_H +#define QLOGGINGCATEGORY_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_CORE_EXPORT QLoggingCategory +{ + Q_DISABLE_COPY(QLoggingCategory) +public: + explicit QLoggingCategory(const char *category); + ~QLoggingCategory(); + + template + bool isEnabled() const + { + return isEnabled(T); + } + + bool isEnabled(QtMsgType type) const; + void setEnabled(QtMsgType type, bool enable); + + const char *categoryName() const { return name; } + + // allows usage of both factory method and variable in qCX macros + QLoggingCategory &operator()() { return *this; } + + static QLoggingCategory &defaultCategory(); + + typedef void (*CategoryFilter)(QLoggingCategory*); + static CategoryFilter installFilter(CategoryFilter); + + static void setFilterRules(const QString &rules); + +private: + const char *name; + + bool enabledDebug; + bool enabledWarning; + bool enabledCritical; + + friend class QLoggingRegistry; +}; + +template <> +inline bool QLoggingCategory::isEnabled() const +{ + return enabledDebug; +} + +template <> +inline bool QLoggingCategory::isEnabled() const +{ + return enabledWarning; +} + +template <> +inline bool QLoggingCategory::isEnabled() const +{ + return enabledCritical; +} + +#define Q_DECLARE_LOGGING_CATEGORY(name) \ + extern QLoggingCategory &name(); + +// relies on QLoggingCategory(QString) being thread safe! +#define Q_LOGGING_CATEGORY(name, string) \ + QLoggingCategory &name() \ + { \ + static QLoggingCategory category(string); \ + return category; \ + } + +#define qCDebug(category) \ + for (bool enabled = category().isEnabled(); enabled; enabled = false) \ + QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO, category().categoryName()).debug() +#define qCWarning(category) \ + for (bool enabled = category().isEnabled(); enabled; enabled = false) \ + QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO, category().categoryName()).warning() +#define qCCritical(category) \ + for (bool enabled = category().isEnabled(); enabled; enabled = false) \ + QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO, category().categoryName()).critical() + +#if defined(QT_NO_DEBUG_OUTPUT) +# undef qCDebug +# define qCDebug(category) QT_NO_QDEBUG_MACRO() +#endif +#if defined(QT_NO_WARNING_OUTPUT) +# undef qCWarning +# define qCWarning(category) QT_NO_QWARNING_MACRO() +#endif + +QT_END_NAMESPACE + +#endif // QLOGGINGCATEGORY_H diff --git a/src/corelib/io/qloggingcategory_p.h b/src/corelib/io/qloggingcategory_p.h new file mode 100644 index 0000000000..7802f017fd --- /dev/null +++ b/src/corelib/io/qloggingcategory_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLOGGINGCATEGORY_P_H +#define QLOGGINGCATEGORY_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. +// + +#include + +QT_BEGIN_NAMESPACE + +// unique pointer to default category +// (allows to compare for pointers instead of strings) +extern const char qtDefaultCategoryName[]; + +QT_END_NAMESPACE + +#endif // QLOGGINGCATEGORY_P_H diff --git a/src/corelib/io/qloggingregistry.cpp b/src/corelib/io/qloggingregistry.cpp new file mode 100644 index 0000000000..a82e6f65f4 --- /dev/null +++ b/src/corelib/io/qloggingregistry.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qloggingregistry_p.h" +#include "qloggingcategory_p.h" + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QLoggingRegistry, qtLoggingRegistry) + +/*! + \internal + Constructs a logging rule with default values. +*/ +QLoggingRule::QLoggingRule() : + flags(Invalid), + enabled(false) +{ +} + +/*! + \internal + Constructs a logging rule. +*/ +QLoggingRule::QLoggingRule(const QString &pattern, bool enabled) : + pattern(pattern), + flags(Invalid), + enabled(enabled) +{ + parse(); +} + +/*! + \internal + Return value 1 means filter passed, 0 means filter doesn't influence this + category, -1 means category doesn't pass this filter. + */ +int QLoggingRule::pass(const QString &categoryName, QtMsgType msgType) const +{ + QString fullCategory = categoryName; + switch (msgType) { + case QtDebugMsg: + fullCategory += QLatin1String(".debug"); + break; + case QtWarningMsg: + fullCategory += QLatin1String(".warning"); + break; + case QtCriticalMsg: + fullCategory += QLatin1String(".critical"); + break; + default: + break; + } + + if (flags == FullText) { + // can be + // qtproject.org.debug = true + // or + // qtproject.org = true + if (pattern == categoryName + || pattern == fullCategory) + return (enabled ? 1 : -1); + } + + int idx = 0; + if (flags == MidFilter) { + // e.g. *.qtproject* + idx = fullCategory.indexOf(pattern); + if (idx >= 0) + return (enabled ? 1 : -1); + } else { + idx = fullCategory.indexOf(pattern); + if (flags == LeftFilter) { + // e.g. org.qtproject.* + if (idx == 0) + return (enabled ? 1 : -1); + } else if (flags == RightFilter) { + // e.g. *.qtproject + if (idx == (fullCategory.count() - pattern.count())) + return (enabled ? 1 : -1); + } + } + return 0; +} + +/*! + \internal + Parses the category and checks which kind of wildcard the filter can contain. + Allowed is f.ex.: + org.qtproject.logging FullText + org.qtproject.* LeftFilter + *.qtproject RightFilter + *.qtproject* MidFilter + */ +void QLoggingRule::parse() +{ + int index = pattern.indexOf(QLatin1Char('*')); + if (index < 0) { + flags = FullText; + } else { + flags = Invalid; + if (index == 0) { + flags |= RightFilter; + pattern = pattern.remove(0, 1); + index = pattern.indexOf(QLatin1Char('*')); + } + if (index == (pattern.length() - 1)) { + flags |= LeftFilter; + pattern = pattern.remove(pattern.length() - 1, 1); + } + } +} + +/*! + \internal + Creates a new QLoggingRules object. +*/ +QLoggingRulesParser::QLoggingRulesParser(QLoggingRegistry *registry) : + registry(registry) +{ +} + +/*! + \internal + Sets logging rules string. +*/ +void QLoggingRulesParser::setRules(const QString &content) +{ + QString content_ = content; + QTextStream stream(&content_, QIODevice::ReadOnly); + parseRules(stream); +} + +/*! + \internal + Parses rules out of a QTextStream. +*/ +void QLoggingRulesParser::parseRules(QTextStream &stream) +{ + QVector rules; + + while (!stream.atEnd()) { + QString line = stream.readLine(); + + // Remove all whitespace from line + line = line.simplified(); + line.remove(QLatin1Char(' ')); + + const QStringList pair = line.split(QLatin1Char('=')); + if (pair.count() == 2) { + const QString pattern = pair.at(0); + bool enabled = (QString::compare(pair.at(1), + QLatin1String("true"), + Qt::CaseInsensitive) == 0); + rules.append(QLoggingRule(pattern, enabled)); + } + } + + registry->setRules(rules); +} + +/*! + \internal + QLoggingPrivate constructor + */ +QLoggingRegistry::QLoggingRegistry() + : rulesParser(this), + categoryFilter(defaultCategoryFilter) +{ +} + +/*! + \internal + Registers a category object. + + This method might be called concurrently for the same category object. +*/ +void QLoggingRegistry::registerCategory(QLoggingCategory *cat) +{ + QMutexLocker locker(®istryMutex); + + if (!categories.contains(cat)) { + categories.append(cat); + (*categoryFilter)(cat); + } +} + +/*! + \internal + Unregisters a category object. +*/ +void QLoggingRegistry::unregisterCategory(QLoggingCategory *cat) +{ + QMutexLocker locker(®istryMutex); + + categories.removeOne(cat); +} + +/*! + \internal + Activates a new set of logging rules for the default filter. +*/ +void QLoggingRegistry::setRules(const QVector &rules_) +{ + QMutexLocker locker(®istryMutex); + + rules = rules_; + + if (categoryFilter != defaultCategoryFilter) + return; + + foreach (QLoggingCategory *cat, categories) + (*categoryFilter)(cat); +} + +/*! + \internal + Installs a custom filter rule. +*/ +QLoggingCategory::CategoryFilter +QLoggingRegistry::installFilter(QLoggingCategory::CategoryFilter filter) +{ + QMutexLocker locker(®istryMutex); + + if (filter == 0) + filter = defaultCategoryFilter; + + QLoggingCategory::CategoryFilter old = categoryFilter; + categoryFilter = filter; + + foreach (QLoggingCategory *cat, categories) + (*categoryFilter)(cat); + + return old; +} + +QLoggingRegistry *QLoggingRegistry::instance() +{ + return qtLoggingRegistry(); +} + +/*! + \internal + Updates category settings according to rules. +*/ +void QLoggingRegistry::defaultCategoryFilter(QLoggingCategory *cat) +{ + // QLoggingCategory() normalizes all "default" strings + // to qtDefaultCategoryName + bool debug = (cat->categoryName() == qtDefaultCategoryName); + bool warning = true; + bool critical = true; + + QString categoryName = QLatin1String(cat->categoryName()); + QLoggingRegistry *reg = QLoggingRegistry::instance(); + foreach (const QLoggingRule &item, reg->rules) { + int filterpass = item.pass(categoryName, QtDebugMsg); + if (filterpass != 0) + debug = (filterpass > 0); + filterpass = item.pass(categoryName, QtWarningMsg); + if (filterpass != 0) + warning = (filterpass > 0); + filterpass = item.pass(categoryName, QtCriticalMsg); + if (filterpass != 0) + critical = (filterpass > 0); + } + + cat->setEnabled(QtDebugMsg, debug); + cat->setEnabled(QtWarningMsg, warning); + cat->setEnabled(QtCriticalMsg, critical); +} + + +QT_END_NAMESPACE diff --git a/src/corelib/io/qloggingregistry_p.h b/src/corelib/io/qloggingregistry_p.h new file mode 100644 index 0000000000..cbf7aecc4f --- /dev/null +++ b/src/corelib/io/qloggingregistry_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLOGGINGREGISTRY_P_H +#define QLOGGINGREGISTRY_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. +// + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QLoggingRule +{ +public: + QLoggingRule(); + QLoggingRule(const QString &pattern, bool enabled); + int pass(const QString &categoryName, QtMsgType type) const; + + enum PatternFlag { + Invalid = 0x0, + FullText = 0x1, + LeftFilter = 0x2, + RightFilter = 0x4, + MidFilter = LeftFilter | RightFilter + }; + Q_DECLARE_FLAGS(PatternFlags, PatternFlag) + + QString pattern; + PatternFlags flags; + bool enabled; + +private: + void parse(); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QLoggingRule::PatternFlags) +Q_DECLARE_TYPEINFO(QLoggingRule, Q_MOVABLE_TYPE); + +class QLoggingRulesParser +{ +private: + explicit QLoggingRulesParser(class QLoggingRegistry *logging); + +public: + void setRules(const QString &content); + +private: + void parseRules(QTextStream &stream); + QLoggingRegistry *registry; + + friend class QLoggingRegistry; +}; + +class QLoggingRegistry +{ +public: + QLoggingRegistry(); + + void registerCategory(QLoggingCategory *category); + void unregisterCategory(QLoggingCategory *category); + + void setRules(const QVector &rules); + + QLoggingCategory::CategoryFilter + installFilter(QLoggingCategory::CategoryFilter filter); + + static QLoggingRegistry *instance(); + + QLoggingRulesParser rulesParser; + +private: + static void defaultCategoryFilter(QLoggingCategory *category); + + QMutex registryMutex; + QVector rules; + QList categories; + QLoggingCategory::CategoryFilter categoryFilter; +}; + +QT_END_NAMESPACE + +#endif // QLOGGINGREGISTRY_P_H diff --git a/tests/auto/corelib/io/io.pro b/tests/auto/corelib/io/io.pro index a30048e250..41d7e7e08b 100644 --- a/tests/auto/corelib/io/io.pro +++ b/tests/auto/corelib/io/io.pro @@ -16,6 +16,7 @@ SUBDIRS=\ qiodevice \ qipaddress \ qlockfile \ + qloggingcategory \ qnodebug \ qprocess \ qprocess-noapplication \ diff --git a/tests/auto/corelib/io/qloggingcategory/qloggingcategory.pro b/tests/auto/corelib/io/qloggingcategory/qloggingcategory.pro new file mode 100644 index 0000000000..8492daefc1 --- /dev/null +++ b/tests/auto/corelib/io/qloggingcategory/qloggingcategory.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +TARGET = tst_qloggingcategory + +CONFIG += testcase +QT = core core-private testlib + +SOURCES += tst_qloggingcategory.cpp diff --git a/tests/auto/corelib/io/qloggingcategory/tst_qloggingcategory.cpp b/tests/auto/corelib/io/qloggingcategory/tst_qloggingcategory.cpp new file mode 100644 index 0000000000..7ddb221402 --- /dev/null +++ b/tests/auto/corelib/io/qloggingcategory/tst_qloggingcategory.cpp @@ -0,0 +1,818 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +Q_LOGGING_CATEGORY(TST_LOG, "tst.log") +Q_LOGGING_CATEGORY(TST_LOG1, "tst.log1") +Q_LOGGING_CATEGORY(Digia_Oslo_Office_com, "Digia.Oslo.Office.com") +Q_LOGGING_CATEGORY(Digia_Oulu_Office_com, "Digia.Oulu.Office.com") +Q_LOGGING_CATEGORY(Digia_Berlin_Office_com, "Digia.Berlin.Office.com") + +QT_USE_NAMESPACE + +QtMessageHandler oldMessageHandler; +QString logMessage; +bool multithreadtest = false; +QStringList threadtest; +QMutex threadmutex; +bool usedefaultformat = false; + +QByteArray qMyMessageFormatString(QtMsgType type, const QMessageLogContext &context, + const QString &str) +{ + QByteArray message; + if (!usedefaultformat) { + message.append(context.category); + switch (type) { + case QtDebugMsg: message.append(".debug"); break; + case QtWarningMsg: message.append(".warning"); break; + case QtCriticalMsg:message.append(".critical"); break; + case QtFatalMsg: message.append(".fatal"); break; + } + message.append(": "); + message.append(qPrintable(str)); + } else { + message.append(qPrintable(str)); + } + + return message.simplified(); +} + +static void myCustomMessageHandler(QtMsgType type, + const QMessageLogContext &context, + const QString &msg) +{ + QMutexLocker locker(&threadmutex); + logMessage = qMyMessageFormatString(type, context, msg); + if (multithreadtest) + threadtest.append(logMessage); +} + +class Configuration +{ +public: + Configuration() + { + } + + void addKey(const QString &key, bool val){ + // Old key values gets updated + _values.insert(key, (val ? "true" : "false")); + if (!_configitemEntryOrder.contains(key)) + _configitemEntryOrder.append(key); + } + + void addKey(const QString &key, const QString &val){ + // Old key values gets updated + _values.insert(key, val); + if (!_configitemEntryOrder.contains(key)) + _configitemEntryOrder.append(key); + } + + QByteArray array() + { + QString ret; + QTextStream out(&ret); + for (int a = 0; a < _configitemEntryOrder.count(); a++) { + out << _configitemEntryOrder[a] + << " = " + << _values.value(_configitemEntryOrder[a]) << endl; + } + out.flush(); + return ret.toLatin1(); + } + + void clear() + { + _values.clear(); + _configitemEntryOrder.clear(); + } + +private: + QMap _values; + QStringList _configitemEntryOrder; +}; + +static Configuration configuration1; +static Configuration configuration2; + +class LogThread : public QThread +{ + Q_OBJECT + +public: + LogThread(const QString &logtext, Configuration *configuration) + : _logtext(logtext), _configuration(configuration) + {} +protected: + void run() + { + for (int i = 0; i < 2000; i++) { + _configuration->addKey("Digia*", true); + QByteArray arr = _configuration->array(); + QLoggingCategory::setFilterRules(arr); + qCDebug(Digia_Oslo_Office_com) << "Oslo " << _logtext << " :true"; + _configuration->addKey("Digia*", false); + arr = _configuration->array(); + QLoggingCategory::setFilterRules(arr); + qCDebug(Digia_Oslo_Office_com) << "Oslo " << _logtext << " :false"; + + _configuration->addKey("Digia*", true); + arr = _configuration->array(); + QLoggingCategory::setFilterRules(arr); + qCDebug(Digia_Berlin_Office_com) << "Berlin " << _logtext << " :true"; + _configuration->addKey("Digia*", false); + arr = _configuration->array(); + QLoggingCategory::setFilterRules(arr); + qCDebug(Digia_Berlin_Office_com) << "Berlin " << _logtext << " :false"; + + _configuration->addKey("Digia*", true); + arr = _configuration->array(); + QLoggingCategory::setFilterRules(arr); + qCDebug(Digia_Oulu_Office_com) << "Oulu " << _logtext << " :true"; + _configuration->addKey("Digia*", false); + arr = _configuration->array(); + QLoggingCategory::setFilterRules(arr); + qCDebug(Digia_Oulu_Office_com) << "Oulu " << _logtext << " :false"; + } + } + +public: + QString _logtext; + Configuration *_configuration; +}; + +inline QString cleanLogLine(const QString &qstring) +{ + QString buf = qstring; + buf.remove("../"); + buf.remove("qlog/"); + QString ret; + for (int i = 0; i < buf.length(); i++) { + if (buf[i] >= '!' && buf[i] <= 'z') + ret += buf[i]; + } + return ret; +} + + +QStringList customCategoryFilterArgs; +static void customCategoryFilter(QLoggingCategory *category) +{ + customCategoryFilterArgs << QLatin1String(category->categoryName()); + // invert debug + category->setEnabled(QtDebugMsg, !category->isEnabled(QtDebugMsg)); +} + +class tst_QLogging : public QObject +{ + Q_OBJECT + +private: + Configuration *_config; + QStringList logEntries; + +private slots: + void initTestCase() + { + qputenv("QT_MESSAGE_PATTERN", QByteArray("%{category}: %{type},%{message}")); + oldMessageHandler = qInstallMessageHandler(myCustomMessageHandler); + // Create configuration + _config = new Configuration(); + } + + void QLoggingCategory_categoryName() + { + logMessage.clear(); + QCOMPARE(QString::fromLatin1(QLoggingCategory::defaultCategory().categoryName()), + QStringLiteral("default")); + + QLoggingCategory defaultCategory("default"); + QCOMPARE(QString::fromLatin1(defaultCategory.categoryName()), + QStringLiteral("default")); + + QLoggingCategory nullCategory(0); + QCOMPARE(QByteArray(nullCategory.categoryName()), QByteArray("default")); + + // we rely on the same pointer for any "default" category + QCOMPARE(QLoggingCategory::defaultCategory().categoryName(), + defaultCategory.categoryName()); + QCOMPARE(defaultCategory.categoryName(), + nullCategory.categoryName()); + + QLoggingCategory customCategory("custom"); + QCOMPARE(QByteArray(customCategory.categoryName()), QByteArray("custom")); + + QLoggingCategory emptyCategory(""); + QCOMPARE(QByteArray(emptyCategory.categoryName()), QByteArray("")); + + // make sure nothing has printed warnings + QVERIFY(logMessage.isEmpty()); + } + + void QLoggingCategory_isEnabled() + { + logMessage.clear(); + + QCOMPARE(QLoggingCategory::defaultCategory().isEnabled(), true); + QCOMPARE(QLoggingCategory::defaultCategory().isEnabled(QtDebugMsg), true); + QCOMPARE(QLoggingCategory::defaultCategory().isEnabled(), true); + QCOMPARE(QLoggingCategory::defaultCategory().isEnabled(QtWarningMsg), true); + QCOMPARE(QLoggingCategory::defaultCategory().isEnabled(), true); + QCOMPARE(QLoggingCategory::defaultCategory().isEnabled(QtCriticalMsg), true); + + QLoggingCategory defaultCategory("default"); + QCOMPARE(defaultCategory.isEnabled(), true); + QCOMPARE(defaultCategory.isEnabled(QtDebugMsg), true); + QCOMPARE(defaultCategory.isEnabled(), true); + QCOMPARE(defaultCategory.isEnabled(QtWarningMsg), true); + QCOMPARE(defaultCategory.isEnabled(), true); + QCOMPARE(defaultCategory.isEnabled(QtCriticalMsg), true); + + QLoggingCategory customCategory("custom"); + QCOMPARE(customCategory.isEnabled(), false); + QCOMPARE(customCategory.isEnabled(QtDebugMsg), false); + QCOMPARE(customCategory.isEnabled(), true); + QCOMPARE(customCategory.isEnabled(QtWarningMsg), true); + QCOMPARE(customCategory.isEnabled(), true); + QCOMPARE(customCategory.isEnabled(QtCriticalMsg), true); + + // make sure nothing has printed warnings + QVERIFY(logMessage.isEmpty()); + } + + void QLoggingCategory_setEnabled() + { + logMessage.clear(); + + QCOMPARE(QLoggingCategory::defaultCategory().isEnabled(), true); + + QLoggingCategory::defaultCategory().setEnabled(QtDebugMsg, false); + QCOMPARE(QLoggingCategory::defaultCategory().isEnabled(), false); + QLoggingCategory::defaultCategory().setEnabled(QtDebugMsg, true); + + // make sure nothing has printed warnings + QVERIFY(logMessage.isEmpty()); + + } + + void QLoggingCategory_installFilter() + { + QVERIFY(QLoggingCategory::defaultCategory().isEnabled()); + + QLoggingCategory::CategoryFilter defaultFilter = + QLoggingCategory::installFilter(customCategoryFilter); + QVERIFY(defaultFilter); + customCategoryFilterArgs.clear(); + QVERIFY(!QLoggingCategory::defaultCategory().isEnabled()); + + QLoggingCategory cat("custom"); + QCOMPARE(customCategoryFilterArgs, QStringList() << "custom"); + QVERIFY(cat.isEnabled()); + customCategoryFilterArgs.clear(); + + // install default filter + QLoggingCategory::CategoryFilter currentFilter = + QLoggingCategory::installFilter(defaultFilter); + QCOMPARE((void*)currentFilter, (void*)customCategoryFilter); + QCOMPARE(customCategoryFilterArgs.size(), 0); + + QVERIFY(QLoggingCategory::defaultCategory().isEnabled()); + QVERIFY(!cat.isEnabled()); + + // install default filter + currentFilter = + QLoggingCategory::installFilter(0); + QCOMPARE((void*)defaultFilter, (void*)currentFilter); + QCOMPARE(customCategoryFilterArgs.size(), 0); + + QVERIFY(QLoggingCategory::defaultCategory().isEnabled()); + QVERIFY(!cat.isEnabled()); + } + + void qDebugMacros() + { + QString buf; + + // Check default debug + buf = QStringLiteral("default.debug: Check debug with no filter active"); + qDebug("%s", "Check debug with no filter active"); + QCOMPARE(logMessage, buf); + + // Check default warning + buf = QStringLiteral("default.warning: Check warning with no filter active"); + qWarning("%s", "Check warning with no filter active"); + QCOMPARE(logMessage, buf); + + // Check default critical + buf = QStringLiteral("default.critical: Check critical with no filter active"); + qCritical("%s", "Check critical with no filter active"); + QCOMPARE(logMessage, buf); + + // install filter (inverts rules for qtdebug) + QLoggingCategory::installFilter(customCategoryFilter); + + // Check default debug + logMessage.clear(); + qDebug("%s", "Check debug with filter active"); + QCOMPARE(logMessage, QString()); + + // reset to default filter + QLoggingCategory::installFilter(0); + + // Check default debug + buf = QStringLiteral("default.debug: Check debug with no filter active"); + qDebug("%s", "Check debug with no filter active"); + QCOMPARE(logMessage, buf); + } + + void qCDebugMacros() + { + QString buf; + + QLoggingCategory defaultCategory("default"); + // Check default debug + buf = QStringLiteral("default.debug: Check debug with no filter active"); + qCDebug(defaultCategory) << "Check debug with no filter active"; + QCOMPARE(logMessage, buf); + + // Check default warning + buf = QStringLiteral("default.warning: Check warning with no filter active"); + qCWarning(defaultCategory) << "Check warning with no filter active"; + QCOMPARE(logMessage, buf); + + // Check default critical + buf = QStringLiteral("default.critical: Check critical with no filter active"); + qCCritical(defaultCategory) << "Check critical with no filter active"; + QCOMPARE(logMessage, buf); + + + QLoggingCategory customCategory("custom"); + // Check custom debug + logMessage.clear(); + qCDebug(customCategory) << "Check debug with no filter active"; + QCOMPARE(logMessage, QString()); + + // Check custom warning + buf = QStringLiteral("custom.warning: Check warning with no filter active"); + qCWarning(customCategory) << "Check warning with no filter active"; + QCOMPARE(logMessage, buf); + + // Check custom critical + buf = QStringLiteral("custom.critical: Check critical with no filter active"); + qCCritical(customCategory) << "Check critical with no filter active"; + QCOMPARE(logMessage, buf); + + // install filter (inverts rules for qtdebug) + QLoggingCategory::installFilter(customCategoryFilter); + + // Check custom debug + buf = QStringLiteral("custom.debug: Check debug with filter active"); + qCDebug(customCategory) << "Check debug with filter active"; + QCOMPARE(logMessage, buf); + + // reset to default filter + QLoggingCategory::installFilter(0); + + // Check custom debug + logMessage.clear(); + qCDebug(customCategory) << "Check debug with no filter active"; + QCOMPARE(logMessage, QString()); + } + + void checkLegacyMessageLogger() + { + usedefaultformat = true; + // This should just not crash. + QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).debug() << "checkLegacyMessageLogger1"; + QCOMPARE(logMessage, QStringLiteral("checkLegacyMessageLogger1")); + QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).warning() << "checkLegacyMessageLogger2"; + QCOMPARE(logMessage, QStringLiteral("checkLegacyMessageLogger2")); + QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).critical() << "checkLegacyMessageLogger3"; + QCOMPARE(logMessage, QStringLiteral("checkLegacyMessageLogger3")); + usedefaultformat = false; + } + + // Check the Debug, Warning and critical without having category active. should be active. + void checkNoCategoryLogActive() + { + // Check default debug + QString buf = QStringLiteral("default.debug: Check default Debug with no log active"); + qDebug() << "Check default Debug with no log active"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Check default warning + buf = QStringLiteral("default.warning: Check default Warning with no log active"); + qWarning() << "Check default Warning with no log active"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Check default critical + buf = QStringLiteral("default.critical: Check default Critical with no log active"); + qCritical() << "Check default Critical with no log active"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Check category debug + logMessage = "should not change"; + buf = logMessage; + qCDebug(TST_LOG) << "Check category Debug with no log active"; + QCOMPARE(logMessage, buf); + + // Check default warning + buf = QStringLiteral("tst.log.warning: Check category Warning with no log active"); + qCWarning(TST_LOG) << "Check category Warning with no log active"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Check default critical + buf = QStringLiteral("tst.log.critical: Check category Critical with no log active"); + qCCritical(TST_LOG) << "Check category Critical with no log active"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + } + + void writeCategoryLogs() + { + usedefaultformat = false; + // Activate TST_LOG category + logMessage = ""; + _config->addKey("tst.log", true); + QLoggingCategory::setFilterRules(_config->array()); + QString buf = QStringLiteral("tst.log.debug: Check for default messagePattern"); + qCDebug(TST_LOG) << "Check for default messagePattern"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Activate TST_LOG category with default enabled function info + _config->addKey("tst.log1", true); + QLoggingCategory::setFilterRules(_config->array()); + qCDebug(TST_LOG) << "1"; + buf = QStringLiteral("tst.log.debug: 1"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Write out all different types + qCDebug(TST_LOG) << "DebugType"; + buf = QStringLiteral("tst.log.debug: DebugType"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCWarning(TST_LOG) << "WarningType"; + buf = QStringLiteral("tst.log.warning: WarningType"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCCritical(TST_LOG) << "CriticalType"; + buf = QStringLiteral("tst.log.critical: CriticalType"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + } + + void checkLegacyLogs() + { + logMessage = ""; + qDebug() << "DefaultDebug"; + QString buf = QStringLiteral("default.debug: DefaultDebug"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // debug off by default, warning and critical are on + qWarning() << "DefaultWarning"; + buf = QStringLiteral("default.warning: DefaultWarning"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCritical() << "DefaultCritical"; + buf = QStringLiteral("default.critical: DefaultCritical"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Enable debug + _config->addKey("default.debug", true); + QLoggingCategory::setFilterRules(_config->array()); + + qDebug() << "DefaultDebug1"; + buf = QStringLiteral("default.debug: DefaultDebug1"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qWarning() << "DefaultWarning1"; + buf = QStringLiteral("default.warning: DefaultWarning1"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCritical() << "DefaultCritical1"; + buf = QStringLiteral("default.critical: DefaultCritical1"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Disable warning + _config->addKey("default.warning", false); + QLoggingCategory::setFilterRules(_config->array()); + + qDebug() << "DefaultDebug2"; + buf = QStringLiteral("default.debug: DefaultDebug2"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + logMessage = "no change"; + qWarning() << "DefaultWarning2"; + buf = QStringLiteral("no change"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCritical() << "DefaultCritical2"; + buf = QStringLiteral("default.critical: DefaultCritical2"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Disable critical + _config->addKey("default.critical", false); + _config->addKey("default.debug", false); + QLoggingCategory::setFilterRules(_config->array()); + + logMessage = "no change"; + qDebug() << "DefaultDebug3"; + buf = QStringLiteral("no change"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qWarning() << "DefaultWarning3"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCritical() << "DefaultCritical3"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Enable default logs + _config->addKey("default.critical", true); + _config->addKey("default.warning", true); + _config->addKey("default.debug", true); + QLoggingCategory::setFilterRules(_config->array()); + + // Ensure all are on + qDebug() << "DefaultDebug4"; + buf = QStringLiteral("default.debug: DefaultDebug4"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qWarning() << "DefaultWarning4"; + buf = QStringLiteral("default.warning: DefaultWarning4"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCritical() << "DefaultCritical4"; + buf = QStringLiteral("default.critical: DefaultCritical4"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Disable default log + _config->addKey("default", false); + QLoggingCategory::setFilterRules(_config->array()); + + // Ensure all are off + logMessage = "no change"; + buf = QStringLiteral("no change"); + qDebug() << "DefaultDebug5"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qWarning() << "DefaultWarning5"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCritical() << "DefaultCritical5"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Reset + _config->clear(); + QLoggingCategory::setFilterRules(_config->array()); + } + + void checkFiltering() + { + // Enable default logs + _config->clear(); + _config->addKey("Digia.Oslo.Office.com", false); + _config->addKey("Digia.Oulu.Office.com", false); + _config->addKey("Digia.Berlin.Office.com", false); + _config->addKey("MessagePattern", QString("%{category}: %{message}")); + QLoggingCategory::setFilterRules(_config->array()); + + logMessage = "no change"; + QString buf = QStringLiteral("no change"); + qCDebug(Digia_Oslo_Office_com) << "Digia.Oslo.Office.com 1"; + qCDebug(Digia_Oulu_Office_com) << "Digia.Oulu.Office.com 1"; + qCDebug(Digia_Berlin_Office_com) << "Digia.Berlin.Office.com 1"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + _config->addKey("Digia.Oslo.Office.com", true); + _config->addKey("Digia.Oulu.Office.com", true); + _config->addKey("Digia.Berlin.Office.com", true); + QLoggingCategory::setFilterRules(_config->array()); + + qCDebug(Digia_Oslo_Office_com) << "Digia.Oslo.Office.com 2"; + buf = QStringLiteral("Digia.Oslo.Office.com.debug: Digia.Oslo.Office.com 2"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCDebug(Digia_Oulu_Office_com) << "Digia.Oulu.Office.com 2"; + buf = QStringLiteral("Digia.Oulu.Office.com.debug: Digia.Oulu.Office.com 2"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCDebug(Digia_Berlin_Office_com) << "Digia.Berlin.Office.com 2"; + buf = QStringLiteral("Digia.Berlin.Office.com.debug: Digia.Berlin.Office.com 2"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Check right filter + _config->addKey("Digia.Oslo.Office.com", false); + _config->addKey("Digia.Oulu.Office.com", false); + _config->addKey("Digia.Berlin.Office.com", false); + _config->addKey("*Office.com*", true); + QLoggingCategory::setFilterRules(_config->array()); + + qCDebug(Digia_Oslo_Office_com) << "Digia.Oslo.Office.com 3"; + buf = QStringLiteral("Digia.Oslo.Office.com.debug: Digia.Oslo.Office.com 3"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCDebug(Digia_Oulu_Office_com) << "Digia.Oulu.Office.com 3"; + buf = QStringLiteral("Digia.Oulu.Office.com.debug: Digia.Oulu.Office.com 3"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCDebug(Digia_Berlin_Office_com) << "Digia.Berlin.Office.com 3"; + buf = QStringLiteral("Digia.Berlin.Office.com.debug: Digia.Berlin.Office.com 3"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Check left filter + _config->addKey("*Office.com*", false); + _config->addKey("*Office.com.debug", true); + QLoggingCategory::setFilterRules(_config->array()); + + qCDebug(Digia_Oslo_Office_com) << "Debug: Digia.Oslo.Office.com 4"; + buf = QStringLiteral("Digia.Oslo.Office.com.debug: Debug: Digia.Oslo.Office.com 4"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + logMessage = "no change"; + buf = QStringLiteral("no change"); + qCWarning(Digia_Oulu_Office_com) << "Warning: Digia.Oulu.Office.com 4"; + qCCritical(Digia_Berlin_Office_com) << "Critical: Digia.Berlin.Office.com 4"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Check right filter + _config->addKey("*Office.com.debug", false); + _config->addKey("Digia.*", true); + QLoggingCategory::setFilterRules(_config->array()); + + qCDebug(Digia_Oslo_Office_com) << "Debug: Digia.Oslo.Office.com 5"; + buf = QStringLiteral("Digia.Oslo.Office.com.debug: Debug: Digia.Oslo.Office.com 5"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCWarning(Digia_Oulu_Office_com) << "Warning: Digia.Oulu.Office.com 5"; + buf = QStringLiteral("Digia.Oulu.Office.com.warning: Warning: Digia.Oulu.Office.com 5"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCCritical(Digia_Berlin_Office_com) << "Critical: Digia.Berlin.Office.com 5"; + buf = QStringLiteral("Digia.Berlin.Office.com.critical: Critical: Digia.Berlin.Office.com 5"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // Check mid filter + _config->addKey("Digia.*", false); + QLoggingCategory::setFilterRules(_config->array()); + + logMessage = "no change"; + buf = QStringLiteral("no change"); + qCDebug(Digia_Oslo_Office_com) << "Debug: Digia.Oslo.Office.com 6"; + qCWarning(Digia_Oulu_Office_com) << "Warning: Digia.Oulu.Office.com 6"; + qCCritical(Digia_Berlin_Office_com) << "Critical: Digia.Berlin.Office.com 6"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + _config->addKey("*.Office.*", true); + QLoggingCategory::setFilterRules(_config->array()); + + qCDebug(Digia_Oslo_Office_com) << "Debug: Digia.Oslo.Office.com 7"; + buf = QStringLiteral("Digia.Oslo.Office.com.debug: Debug: Digia.Oslo.Office.com 7"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCWarning(Digia_Oulu_Office_com) << "Warning: Digia.Oulu.Office.com 7"; + buf = QStringLiteral("Digia.Oulu.Office.com.warning: Warning: Digia.Oulu.Office.com 7"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + qCCritical(Digia_Berlin_Office_com) << "Critical: Digia.Berlin.Office.com 7"; + buf = QStringLiteral("Digia.Berlin.Office.com.critical: Critical: Digia.Berlin.Office.com 7"); + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + } + + void checkLogWithCategoryObject() + { + _config->clear(); + _config->addKey("LoggingCategoryObject", true); + QLoggingCategory *pcategorybject = 0; + QLoggingCategory::setFilterRules(_config->array()); + { + QLoggingCategory mycategoryobject("LoggingCategoryObject"); + pcategorybject = &mycategoryobject; + logMessage = "no change"; + + QString buf = QStringLiteral("LoggingCategoryObject.debug: My Category Object"); + qCDebug(mycategoryobject) << "My Category Object"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + buf = QStringLiteral("LoggingCategoryObject.warning: My Category Object"); + qCWarning(mycategoryobject) << "My Category Object"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + buf = QStringLiteral("LoggingCategoryObject.critical: My Category Object"); + qCCritical(mycategoryobject) << "My Category Object"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + QLoggingCategory mycategoryobject2("LoggingCategoryObject"); + buf = QStringLiteral("LoggingCategoryObject.debug: My Category Object"); + qCDebug(mycategoryobject) << "My Category Object"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + buf = QStringLiteral("LoggingCategoryObject.warning: My Category Object"); + qCWarning(mycategoryobject) << "My Category Object"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + buf = QStringLiteral("LoggingCategoryObject.critical: My Category Object"); + qCCritical(mycategoryobject) << "My Category Object"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + } + } + + void checkEmptyCategoryName() + { + // "" -> custom category + QLoggingCategory mycategoryobject1(""); + logMessage = "no change"; + QString buf = QStringLiteral("no change"); + qCDebug(mycategoryobject1) << "My Category Object"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + + // 0 -> default category + QLoggingCategory mycategoryobject2(0); + buf = QStringLiteral("default.debug:MyCategoryObject"); + qCDebug(mycategoryobject2) << "My Category Object"; + QCOMPARE(cleanLogLine(logMessage), cleanLogLine(buf)); + } + + void checkMultithreading() + { + multithreadtest = true; + // Init two configurations, one for each thread + configuration1.addKey("Digia*", true); + configuration2.addKey("Digia*", true); + QByteArray arr = configuration1.array(); + QLoggingCategory::setFilterRules(arr); + + LogThread thgread1(QString("from Thread 1"), &configuration1); + LogThread thgread2(QString("from Thread 2"), &configuration2); + + // Writing out stuff from 2 different threads into the same areas + thgread1.start(); + thgread2.start(); + thgread1.wait(); + thgread2.wait(); + + // Check if each log line is complete + QStringList compareagainst; + QString buf = QStringLiteral("Digia.Oslo.Office.com.debug: Oslo \"from Thread 1\" :true"); + compareagainst.append(cleanLogLine(buf)); + buf = QStringLiteral("Digia.Oulu.Office.com.debug: Oulu \"from Thread 1\" :true"); + compareagainst.append(cleanLogLine(buf)); + buf = QStringLiteral("Digia.Berlin.Office.com.debug: Berlin \"from Thread 1\" :true"); + compareagainst.append(cleanLogLine(buf)); + + buf = QStringLiteral("Digia.Oslo.Office.com.debug: Oslo \"from Thread 1\" :false"); + compareagainst.append(cleanLogLine(buf)); + buf = QStringLiteral("Digia.Oulu.Office.com.debug: Oulu \"from Thread 1\" :false"); + compareagainst.append(cleanLogLine(buf)); + buf = QStringLiteral("Digia.Berlin.Office.com.debug: Berlin \"from Thread 1\" :false"); + compareagainst.append(cleanLogLine(buf)); + + buf = QStringLiteral("Digia.Oslo.Office.com.debug: Oslo \"from Thread 2\" :true"); + compareagainst.append(cleanLogLine(buf)); + buf = QStringLiteral("Digia.Oulu.Office.com.debug: Oulu \"from Thread 2\" :true"); + compareagainst.append(cleanLogLine(buf)); + buf = QStringLiteral("Digia.Berlin.Office.com.debug: Berlin \"from Thread 2\" :true"); + compareagainst.append(cleanLogLine(buf)); + + buf = QStringLiteral("Digia.Oslo.Office.com.debug: Oslo \"from Thread 2\" :false"); + compareagainst.append(cleanLogLine(buf)); + buf = QStringLiteral("Digia.Oulu.Office.com.debug: Oulu \"from Thread 2\" :false"); + compareagainst.append(cleanLogLine(buf)); + buf = QStringLiteral("Digia.Berlin.Office.com.debug: Berlin \"from Thread 2\" :false"); + compareagainst.append(cleanLogLine(buf)); + + for (int i = 0; i < threadtest.count(); i++) { + if (!compareagainst.contains(cleanLogLine(threadtest[i]))){ + fprintf(stdout, "%s\r\n", threadtest[i].toLatin1().constData()); + QVERIFY2(false, "Multithread log is not complete!"); + } + } + } + + void cleanupTestCase() + { + delete _config; + qInstallMessageHandler(oldMessageHandler); + } +}; + +QTEST_MAIN(tst_QLogging) + +#include "tst_qloggingcategory.moc" diff --git a/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp b/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp index becff75836..157e42b447 100644 --- a/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp +++ b/tests/auto/corelib/io/qnodebug/tst_qnodebug.cpp @@ -49,6 +49,7 @@ #include #include +#include #include class tst_QNoDebug: public QObject @@ -61,12 +62,16 @@ private slots: void tst_QNoDebug::noDebugOutput() const { + QLoggingCategory cat("custom"); // should do nothing qDebug() << "foo"; + qCDebug(cat) << "foo"; // qWarning still works, though QTest::ignoreMessage(QtWarningMsg, "bar "); + QTest::ignoreMessage(QtWarningMsg, "custom-bar "); qWarning() << "bar"; + qCWarning(cat) << "custom-bar"; } void tst_QNoDebug::streaming() const