Support setting a default severity level for QLoggingCategory

Allow to alter the default configuration for categories by passing a
message type: All message types with lower severity are disabled in this
category.

This is useful for libraries, which shouldn't mess with the category
registry itself: Setting rules, a category filter ... might cause
conflicts and ordering problems, so this API should be reserved to the
specific application.

For the Qt categories, we have code in the default category filter that
disables the 'debug' category. However, this is hardcoded, and there's no
way so far for other libraries to get the same behavior. With this patch
one can get the same behavior:

Q_LOGGING_CATEGORY(DRIVER_USB_EVENTS, "driver.usb.events", QtWarningMsg);

[ChangeLog][QtCore][Logging] Added QtMsgType argument to QLoggingCategory
constructor and Q_LOGGING_CATEGORY macro that controls the default
category configuration.

Change-Id: Ib2902f755f9f7285d79888ec30e8f3cef95ae628
Reviewed-by: Alex Blasche <alexander.blasche@digia.com>
This commit is contained in:
Kai Koehne 2014-07-11 12:36:30 +02:00
parent c4a2d6ca1e
commit ea34893b8f
6 changed files with 133 additions and 37 deletions

View File

@ -49,6 +49,9 @@ Q_DECLARE_LOGGING_CATEGORY(driverUsb)
Q_LOGGING_CATEGORY(driverUsb, "driver.usb")
//![1]
//![5]
Q_LOGGING_CATEGORY(driverUsbEvents, "driver.usb.events", QtWarningMsg)
//![5]
// Completely made up example, inspired by en.wikipedia.org/wiki/USB :)
struct UsbEntry {

View File

@ -101,13 +101,21 @@ static void setBoolLane(QBasicAtomicInt *atomic, bool enable, int shift)
\section1 Default category configuration
In the default configuration \l isWarningEnabled() , \l isDebugEnabled() and
\l isCriticalEnabled() will return \c true.
Both the QLoggingCategory constructor and the Q_LOGGING_CATEGORY() macro
accept an optional QtMsgType argument, which disables all message types with
a lower severity. That is, a category declared with
\snippet qloggingcategory/main.cpp 5
will log messages of type \c QtWarningMsg, \c QtCriticalMsg, \c QtFatalMsg, but will
ignore messages of type \c QtDebugMsg.
If no argument is passed, all messages will be logged.
\section1 Configuring Categories
Categories can be centrally configured by either setting logging rules,
or by installing a custom filter.
The default configuration of categories can be overridden either by setting logging
rules, or by installing a custom filter.
\section2 Logging Rules
@ -183,13 +191,33 @@ static void setBoolLane(QBasicAtomicInt *atomic, bool enable, int shift)
/*!
Constructs a QLoggingCategory object with the provided \a category name.
The object becomes the local identifier for the category.
All message types for this category are enabled by default.
If \a category is \c{0}, the category name is changed to \c "default".
*/
QLoggingCategory::QLoggingCategory(const char *category)
: d(0),
name(0)
{
init(category, QtDebugMsg);
}
/*!
Constructs a QLoggingCategory object with the provided \a category name,
and enables all messages with types more severe or equal than \a enableForLevel.
If \a category is \c{0}, the category name is changed to \c "default".
\since 5.4
*/
QLoggingCategory::QLoggingCategory(const char *category, QtMsgType enableForLevel)
: d(0),
name(0)
{
init(category, enableForLevel);
}
void QLoggingCategory::init(const char *category, QtMsgType severityLevel)
{
enabled.store(0x01010101); // enabledDebug = enabledWarning = enabledCritical = true;
@ -198,14 +226,13 @@ QLoggingCategory::QLoggingCategory(const char *category)
// normalize "default" category name, so that we can just do
// pointer comparison in QLoggingRegistry::updateCategory
if (isDefaultCategory) {
if (isDefaultCategory)
name = qtDefaultCategoryName;
} else {
else
name = category;
}
if (QLoggingRegistry *reg = QLoggingRegistry::instance())
reg->registerCategory(this);
reg->registerCategory(this, severityLevel);
}
/*!
@ -522,7 +549,7 @@ void QLoggingCategory::setFilterRules(const QString &rules)
\since 5.2
Defines a logging category \a name, and makes it configurable under the
\a string identifier.
\a string identifier. By default, all message types are enabled.
Only one translation unit in a library or executable can define a category
with a specific name.
@ -530,4 +557,21 @@ void QLoggingCategory::setFilterRules(const QString &rules)
This macro must be used outside of a class or method.
*/
/*!
\macro Q_LOGGING_CATEGORY(name, string, msgType)
\sa Q_DECLARE_LOGGING_CATEGORY()
\relates QLoggingCategory
\since 5.4
Defines a logging category \a name, and makes it configurable under the
\a string identifier. By default, messages of QtMsgType \a msgType
and more severe are enabled, types with a lower severity are disabled.
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. It is only defined
if variadic macros are supported.
*/
QT_END_NAMESPACE

View File

@ -51,7 +51,9 @@ class Q_CORE_EXPORT QLoggingCategory
{
Q_DISABLE_COPY(QLoggingCategory)
public:
// ### Qt 6: Merge constructors
explicit QLoggingCategory(const char *category);
QLoggingCategory(const char *category, QtMsgType severityLevel);
~QLoggingCategory();
bool isEnabled(QtMsgType type) const;
@ -80,6 +82,8 @@ public:
static void setFilterRules(const QString &rules);
private:
void init(const char *category, QtMsgType severityLevel);
Q_DECL_UNUSED_MEMBER void *d; // reserved for future use
const char *name;
@ -106,16 +110,14 @@ private:
#define Q_DECLARE_LOGGING_CATEGORY(name) \
extern const QLoggingCategory &name();
// relies on QLoggingCategory(QString) being thread safe!
#define Q_LOGGING_CATEGORY(name, string) \
#if defined(Q_COMPILER_VARIADIC_MACROS) || defined(Q_MOC_RUN)
#define Q_LOGGING_CATEGORY(name, ...) \
const QLoggingCategory &name() \
{ \
static const QLoggingCategory category(string); \
static const QLoggingCategory category(__VA_ARGS__); \
return category; \
}
#ifdef Q_COMPILER_VARIADIC_MACROS
#define qCDebug(category, ...) \
for (bool qt_category_enabled = category().isDebugEnabled(); qt_category_enabled; qt_category_enabled = false) \
QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO, category().categoryName()).debug(__VA_ARGS__)
@ -126,14 +128,22 @@ private:
for (bool qt_category_enabled = category().isCriticalEnabled(); qt_category_enabled; qt_category_enabled = false) \
QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO, category().categoryName()).critical(__VA_ARGS__)
#else
#else // defined(Q_COMPILER_VARIADIC_MACROS) || defined(Q_MOC_RUN)
// Optional msgType argument not supported
#define Q_LOGGING_CATEGORY(name, string) \
const QLoggingCategory &name() \
{ \
static const QLoggingCategory category(string); \
return category; \
}
// check for enabled category inside QMessageLogger.
#define qCDebug qDebug
#define qCWarning qWarning
#define qCCritical qCritical
#endif // Q_COMPILER_VARIADIC_MACROS
#endif // Q_COMPILER_VARIADIC_MACROS || defined(Q_MOC_RUN)
#if defined(QT_NO_DEBUG_OUTPUT)
# undef qCDebug

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Copyright (C) 2014 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.
@ -307,12 +307,12 @@ void QLoggingRegistry::init()
This method might be called concurrently for the same category object.
*/
void QLoggingRegistry::registerCategory(QLoggingCategory *cat)
void QLoggingRegistry::registerCategory(QLoggingCategory *cat, QtMsgType enableForLevel)
{
QMutexLocker locker(&registryMutex);
if (!categories.contains(cat)) {
categories.append(cat);
categories.insert(cat, enableForLevel);
(*categoryFilter)(cat);
}
}
@ -324,8 +324,7 @@ void QLoggingRegistry::registerCategory(QLoggingCategory *cat)
void QLoggingRegistry::unregisterCategory(QLoggingCategory *cat)
{
QMutexLocker locker(&registryMutex);
categories.removeOne(cat);
categories.remove(cat);
}
/*!
@ -361,7 +360,7 @@ void QLoggingRegistry::updateRules()
rules = configRules + apiRules + envRules;
foreach (QLoggingCategory *cat, categories)
foreach (QLoggingCategory *cat, categories.keys())
(*categoryFilter)(cat);
}
@ -380,7 +379,7 @@ QLoggingRegistry::installFilter(QLoggingCategory::CategoryFilter filter)
QLoggingCategory::CategoryFilter old = categoryFilter;
categoryFilter = filter;
foreach (QLoggingCategory *cat, categories)
foreach (QLoggingCategory *cat, categories.keys())
(*categoryFilter)(cat);
return old;
@ -397,18 +396,22 @@ QLoggingRegistry *QLoggingRegistry::instance()
*/
void QLoggingRegistry::defaultCategoryFilter(QLoggingCategory *cat)
{
// QLoggingCategory() normalizes "default" strings
// to qtDefaultCategoryName
bool debug = true;
QLoggingRegistry *reg = QLoggingRegistry::instance();
Q_ASSERT(reg->categories.contains(cat));
QtMsgType enableForLevel = reg->categories.value(cat);
bool debug = (enableForLevel == QtDebugMsg) ? true : false;
bool warning = (enableForLevel <= QtWarningMsg) ? true : false;
bool critical = (enableForLevel <= QtCriticalMsg) ? true : false;
// hard-wired implementation of
// qt.*.debug=false
// qt.debug=false
char c;
if (!memcmp(cat->categoryName(), "qt", 2) && (!(c = cat->categoryName()[2]) || c == '.'))
debug = false;
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)

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Copyright (C) 2014 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.
@ -113,7 +113,7 @@ public:
void init();
void registerCategory(QLoggingCategory *category);
void registerCategory(QLoggingCategory *category, QtMsgType enableForLevel);
void unregisterCategory(QLoggingCategory *category);
void setApiRules(const QString &content);
@ -134,7 +134,7 @@ private:
QVector<QLoggingRule> envRules;
QVector<QLoggingRule> apiRules;
QVector<QLoggingRule> rules;
QList<QLoggingCategory*> categories;
QHash<QLoggingCategory*,QtMsgType> categories;
QLoggingCategory::CategoryFilter categoryFilter;
friend class ::tst_QLoggingRegistry;

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Copyright (C) 2014 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.
@ -44,7 +44,6 @@
#include <QLoggingCategory>
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")
@ -278,6 +277,14 @@ private slots:
QCOMPARE(customCategory.isCriticalEnabled(), true);
QCOMPARE(customCategory.isEnabled(QtCriticalMsg), true);
QLoggingCategory onlyWarningsCategory("withType", QtWarningMsg);
QCOMPARE(onlyWarningsCategory.isDebugEnabled(), false);
QCOMPARE(onlyWarningsCategory.isEnabled(QtDebugMsg), false);
QCOMPARE(onlyWarningsCategory.isWarningEnabled(), true);
QCOMPARE(onlyWarningsCategory.isEnabled(QtWarningMsg), true);
QCOMPARE(onlyWarningsCategory.isCriticalEnabled(), true);
QCOMPARE(onlyWarningsCategory.isEnabled(QtCriticalMsg), true);
// make sure nothing has printed warnings
QVERIFY(logMessage.isEmpty());
}
@ -367,6 +374,35 @@ private slots:
QCOMPARE(logMessage, buf);
}
Q_LOGGING_CATEGORY(TST_MACRO_1, "tst.macro.1")
#ifdef Q_COMPILER_VARIADIC_MACROS
Q_LOGGING_CATEGORY(TST_MACRO_2, "tst.macro.2", QtDebugMsg)
Q_LOGGING_CATEGORY(TST_MACRO_3, "tst.macro.3", QtFatalMsg)
#endif
void QLoggingCategoryMacro()
{
const QLoggingCategory &cat1 = TST_MACRO_1();
QCOMPARE(cat1.categoryName(), "tst.macro.1");
QCOMPARE(cat1.isDebugEnabled(), true);
QCOMPARE(cat1.isWarningEnabled(), true);
QCOMPARE(cat1.isCriticalEnabled(), true);
#ifdef Q_COMPILER_VARIADIC_MACROS
const QLoggingCategory &cat2 = TST_MACRO_2();
QCOMPARE(cat2.categoryName(), "tst.macro.2");
QCOMPARE(cat2.isDebugEnabled(), true);
QCOMPARE(cat2.isWarningEnabled(), true);
QCOMPARE(cat2.isCriticalEnabled(), true);
const QLoggingCategory &cat3 = TST_MACRO_3();
QCOMPARE(cat3.categoryName(), "tst.macro.3");
QCOMPARE(cat3.isDebugEnabled(), false);
QCOMPARE(cat3.isWarningEnabled(), false);
QCOMPARE(cat3.isCriticalEnabled(), false);
#endif
}
void qCDebugMacros()
{
QString buf;