summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKai Koehne <kai.koehne@digia.com>2014-02-04 16:35:26 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-02-11 20:59:15 +0100
commita2bfd114938e1fdd5067f2dac812a8d3a5d89fd3 (patch)
tree3a0341ab6973bc827c7e2c0f4787f23820d7adb3
parentdc09a02e3a4ea93388197d7f8a0cfebe194e9886 (diff)
Allow configuration of logging rules from file system
Allow configuration of logging rules from outside of the application, either through a configuration file (.config/QtProject/qtlogging.ini), or through a file specified by a QT_LOGGING_CONF environment variable. The logging rules from the different sources are concatenated: First the rules from QtProject/qtlogging.ini are applied, then QLoggingCategory::setLoggingRules(), finally from the environment. This allows an application to overwrite/augment the system wide rules, and in turn that can be tailored for a specific run by setting a configuration in the environment variable. [ChangeLog][QtCore][Logging] The logging framework can now be configured with an .ini file. Change-Id: I442efde1b7e0a2ebe135c6f6e0a4b656483fe4b1 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r--src/corelib/io/qloggingcategory.cpp73
-rw-r--r--src/corelib/io/qloggingregistry.cpp129
-rw-r--r--src/corelib/io/qloggingregistry_p.h36
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp5
-rw-r--r--tests/auto/corelib/io/io.pro4
-rw-r--r--tests/auto/corelib/io/qloggingregistry/qloggingregistry.pro8
-rw-r--r--tests/auto/corelib/io/qloggingregistry/qtlogging.ini2
-rw-r--r--tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp179
8 files changed, 378 insertions, 58 deletions
diff --git a/src/corelib/io/qloggingcategory.cpp b/src/corelib/io/qloggingcategory.cpp
index 5fa346dce5..168087659f 100644
--- a/src/corelib/io/qloggingcategory.cpp
+++ b/src/corelib/io/qloggingcategory.cpp
@@ -86,12 +86,62 @@ Q_GLOBAL_STATIC_WITH_ARGS(QLoggingCategory, qtDefaultCategory,
In the default configuration \l isWarningEnabled() , \l isDebugEnabled() and
\l isCriticalEnabled() will return \c true.
- \section1 Changing the configuration of a category
+ \section1 Configuring Categories
- Use either \l setFilterRules() or \l installFilter() to
- configure categories, for example
+ Categories can be centrally configured by either setting logging rules,
+ or by installing a custom filter.
- \snippet qloggingcategory/main.cpp 2
+ \section2 Logging Rules
+
+ Logging rules allow to enable or disable logging for categories in a flexible
+ way. Rules are specified in text, where every line must have the format
+
+ \code
+ <category>[.<type>] = true|false
+ \endcode
+
+ \c <category> is the name of the category, potentially with \c{*} as a
+ wildcard symbol as the first or last character (or at both positions).
+ The optional \c <type> must be either \c debug, \c warning, or \c critical.
+ Lines that do not fit to his scheme are ignored.
+
+ Rules are evaluated in text order, from first to last. That is, if two rules
+ apply to a category/type, the rule that comes later is applied.
+
+ Rules can be set via \l setFilterRules(). Since Qt 5.3 logging rules
+ are also automatically loaded from the \c [rules] section of a logging
+ configuration file. Such configuration files are looked up in the QtProject
+ configuration directory, or explicitly set in a \c QT_LOGGING_CONF
+ environment variable.
+
+ Rules set by \l setFilterRules() take precedence over rules specified
+ in the QtProject configuration directory, and can, in turn, be
+ overwritten by rules from the configuration file specified by
+ \c QT_LOGGING_CONF.
+
+ Order of evaluation:
+ \list
+ \li Rules from QtProject/qlogging.ini
+ \li Rules set by \l setFilterRules()
+ \li Rules from file in \c QT_LOGGING_CONF
+ \endlist
+
+ The \c QtProject/qlogging.ini file is looked up in all directories returned
+ by QStandardPaths::GenericConfigLocation, e.g.
+
+ \list
+ \li on Mac OS X: \c ~/Library/Preferences
+ \li on Unix: \c ~/.config, \c /etc/xdg
+ \li on Windows: \c %LOCALAPPDATA%, \c %ProgramData%,
+ \l QCoreApplication::applicationDirPath(),
+ QCoreApplication::applicationDirPath() + \c "/data"
+ \endlist
+
+ \section2 Installing a Custom Filter
+
+ As a lower-level alternative to the text rules you can also implement a
+ custom filter via \l installFilter(). All filter rules are ignored in this
+ case.
\section1 Printing the category
@@ -278,27 +328,18 @@ QLoggingCategory::installFilter(QLoggingCategory::CategoryFilter 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
- <category>[.<type>] = true|false
- \endcode
-
- where \c <category> is the name of the category, potentially with \c{*} as a
- wildcard symbol at the start and/or the end. The optional \c <type> must
- be either \c debug, \c warning, or \c critical.
-
Example:
\snippet qloggingcategory/main.cpp 2
\note The rules might be ignored if a custom category filter is installed
- with \l installFilter().
+ with \l installFilter(), or if the user defined a custom logging
+ configuration file in the \c QT_LOGGING_CONF environment variable.
*/
void QLoggingCategory::setFilterRules(const QString &rules)
{
- QLoggingRegistry::instance()->rulesParser.setRules(rules);
+ QLoggingRegistry::instance()->setApiRules(rules);
}
/*!
diff --git a/src/corelib/io/qloggingregistry.cpp b/src/corelib/io/qloggingregistry.cpp
index fd25ff697e..b8fef16c0e 100644
--- a/src/corelib/io/qloggingregistry.cpp
+++ b/src/corelib/io/qloggingregistry.cpp
@@ -42,6 +42,10 @@
#include "qloggingregistry_p.h"
#include "qloggingcategory_p.h"
+#include <QtCore/qfile.h>
+#include <QtCore/qstandardpaths.h>
+#include <QtCore/qtextstream.h>
+
QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC(QLoggingRegistry, qtLoggingRegistry)
@@ -150,33 +154,38 @@ void QLoggingRule::parse()
}
/*!
+ \class QLoggingSettingsParser
+ \since 5.3
\internal
- Creates a new QLoggingRules object.
+
+ Parses a .ini file with the following format:
+
+ [rules]
+ rule1=[true|false]
+ rule2=[true|false]
+ ...
+
+ [rules] is the default section, and therefore optional.
*/
-QLoggingRulesParser::QLoggingRulesParser(QLoggingRegistry *registry) :
- registry(registry)
-{
-}
/*!
\internal
- Sets logging rules string.
+ Parses configuration from \a content.
*/
-void QLoggingRulesParser::setRules(const QString &content)
+void QLoggingSettingsParser::setContent(const QString &content)
{
QString content_ = content;
QTextStream stream(&content_, QIODevice::ReadOnly);
- parseRules(stream);
+ setContent(stream);
}
/*!
\internal
- Parses rules out of a QTextStream.
+ Parses configuration from \a stream.
*/
-void QLoggingRulesParser::parseRules(QTextStream &stream)
+void QLoggingSettingsParser::setContent(QTextStream &stream)
{
- QVector<QLoggingRule> rules;
-
+ _rules.clear();
while (!stream.atEnd()) {
QString line = stream.readLine();
@@ -184,31 +193,79 @@ void QLoggingRulesParser::parseRules(QTextStream &stream)
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));
+ // comment
+ if (line.startsWith(QLatin1Char(';')))
+ continue;
+
+ if (line.startsWith(QLatin1Char('[')) && line.endsWith(QLatin1Char(']'))) {
+ // new section
+ _section = line.mid(1, line.size() - 2);
+ continue;
}
- }
- registry->setRules(rules);
+ if (_section == QLatin1String("rules")) {
+ int equalPos = line.indexOf(QLatin1Char('='));
+ if ((equalPos != -1)
+ && (line.lastIndexOf(QLatin1Char('=')) == equalPos)) {
+ const QString pattern = line.left(equalPos);
+ const QStringRef value = line.midRef(equalPos + 1);
+ bool enabled = (value.compare(QLatin1String("true"),
+ Qt::CaseInsensitive) == 0);
+ _rules.append(QLoggingRule(pattern, enabled));
+ }
+ }
+ }
}
/*!
\internal
- QLoggingPrivate constructor
+ QLoggingRegistry constructor
*/
QLoggingRegistry::QLoggingRegistry()
- : rulesParser(this),
- categoryFilter(defaultCategoryFilter)
+ : categoryFilter(defaultCategoryFilter)
{
}
/*!
\internal
+ Initializes the rules database by loading
+ .config/QtProject/qtlogging.ini and $QT_LOGGING_CONF.
+ */
+void QLoggingRegistry::init()
+{
+ // get rules from environment
+ const QByteArray rulesFilePath = qgetenv("QT_LOGGING_CONF");
+ if (!rulesFilePath.isEmpty()) {
+ QFile file(QFile::decodeName(rulesFilePath));
+ if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QTextStream stream(&file);
+ QLoggingSettingsParser parser;
+ parser.setContent(stream);
+ envRules = parser.rules();
+ }
+ }
+
+ // get rules from qt configuration
+ QString envPath = QStandardPaths::locate(QStandardPaths::GenericConfigLocation,
+ QStringLiteral("QtProject/qtlogging.ini"));
+ if (!envPath.isEmpty()) {
+ QFile file(envPath);
+ if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QTextStream stream(&file);
+ QLoggingSettingsParser parser;
+ parser.setContent(stream);
+ configRules = parser.rules();
+ }
+ }
+
+ if (!envRules.isEmpty() || !configRules.isEmpty()) {
+ QMutexLocker locker(&registryMutex);
+ updateRules();
+ }
+}
+
+/*!
+ \internal
Registers a category object.
This method might be called concurrently for the same category object.
@@ -236,17 +293,33 @@ void QLoggingRegistry::unregisterCategory(QLoggingCategory *cat)
/*!
\internal
- Activates a new set of logging rules for the default filter.
-*/
-void QLoggingRegistry::setRules(const QVector<QLoggingRule> &rules_)
+ Installs logging rules as specified in \a content.
+ */
+void QLoggingRegistry::setApiRules(const QString &content)
{
+ QLoggingSettingsParser parser;
+ parser.setSection(QStringLiteral("rules"));
+ parser.setContent(content);
+
QMutexLocker locker(&registryMutex);
+ apiRules = parser.rules();
- rules = rules_;
+ updateRules();
+}
+/*!
+ \internal
+ Activates a new set of logging rules for the default filter.
+
+ (The caller must lock registryMutex to make sure the API is thread safe.)
+*/
+void QLoggingRegistry::updateRules()
+{
if (categoryFilter != defaultCategoryFilter)
return;
+ rules = configRules + apiRules + envRules;
+
foreach (QLoggingCategory *cat, categories)
(*categoryFilter)(cat);
}
diff --git a/src/corelib/io/qloggingregistry_p.h b/src/corelib/io/qloggingregistry_p.h
index cbf7aecc4f..d4b97d42b8 100644
--- a/src/corelib/io/qloggingregistry_p.h
+++ b/src/corelib/io/qloggingregistry_p.h
@@ -60,6 +60,8 @@
#include <QtCore/qtextstream.h>
#include <QtCore/qvector.h>
+class tst_QLoggingRegistry;
+
QT_BEGIN_NAMESPACE
class QLoggingRule
@@ -89,45 +91,53 @@ private:
Q_DECLARE_OPERATORS_FOR_FLAGS(QLoggingRule::PatternFlags)
Q_DECLARE_TYPEINFO(QLoggingRule, Q_MOVABLE_TYPE);
-class QLoggingRulesParser
+class Q_AUTOTEST_EXPORT QLoggingSettingsParser
{
-private:
- explicit QLoggingRulesParser(class QLoggingRegistry *logging);
-
public:
- void setRules(const QString &content);
+ void setSection(const QString &section) { _section = section; }
-private:
- void parseRules(QTextStream &stream);
- QLoggingRegistry *registry;
+ void setContent(const QString &content);
+ void setContent(QTextStream &stream);
+
+ QVector<QLoggingRule> rules() const { return _rules; }
- friend class QLoggingRegistry;
+private:
+ QString _section;
+ QVector<QLoggingRule> _rules;
};
-class QLoggingRegistry
+class Q_AUTOTEST_EXPORT QLoggingRegistry
{
public:
QLoggingRegistry();
+ void init();
+
void registerCategory(QLoggingCategory *category);
void unregisterCategory(QLoggingCategory *category);
- void setRules(const QVector<QLoggingRule> &rules);
+ void setApiRules(const QString &content);
QLoggingCategory::CategoryFilter
installFilter(QLoggingCategory::CategoryFilter filter);
static QLoggingRegistry *instance();
- QLoggingRulesParser rulesParser;
-
private:
+ void updateRules();
+
static void defaultCategoryFilter(QLoggingCategory *category);
QMutex registryMutex;
+
+ QVector<QLoggingRule> configRules;
+ QVector<QLoggingRule> envRules;
+ QVector<QLoggingRule> apiRules;
QVector<QLoggingRule> rules;
QList<QLoggingCategory*> categories;
QLoggingCategory::CategoryFilter categoryFilter;
+
+ friend class ::tst_QLoggingRegistry;
};
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index d5d964eaec..f6d4a3b10b 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -55,6 +55,7 @@
#include <qfileinfo.h>
#include <qhash.h>
#include <qmutex.h>
+#include <private/qloggingregistry_p.h>
#include <private/qprocess_p.h>
#include <qstandardpaths.h>
#include <qtextcodec.h>
@@ -722,6 +723,10 @@ void QCoreApplication::init()
Q_ASSERT_X(!self, "QCoreApplication", "there should be only one application object");
QCoreApplication::self = this;
+#ifndef QT_BOOTSTRAPPED
+ QLoggingRegistry::instance()->init();
+#endif
+
#ifndef QT_NO_QOBJECT
// use the event dispatcher created by the app programmer (if any)
if (!QCoreApplicationPrivate::eventDispatcher)
diff --git a/tests/auto/corelib/io/io.pro b/tests/auto/corelib/io/io.pro
index 259463f976..ba055a4f3f 100644
--- a/tests/auto/corelib/io/io.pro
+++ b/tests/auto/corelib/io/io.pro
@@ -17,6 +17,7 @@ SUBDIRS=\
qipaddress \
qlockfile \
qloggingcategory \
+ qloggingregistry \
qnodebug \
qprocess \
qprocess-noapplication \
@@ -51,7 +52,8 @@ SUBDIRS=\
qabstractfileengine \
qfileinfo \
qipaddress \
- qurlinternal
+ qurlinternal \
+ qloggingregistry
win32:!contains(QT_CONFIG, private_tests): SUBDIRS -= \
qfilesystementry
diff --git a/tests/auto/corelib/io/qloggingregistry/qloggingregistry.pro b/tests/auto/corelib/io/qloggingregistry/qloggingregistry.pro
new file mode 100644
index 0000000000..c6c4caace3
--- /dev/null
+++ b/tests/auto/corelib/io/qloggingregistry/qloggingregistry.pro
@@ -0,0 +1,8 @@
+TEMPLATE = app
+TARGET = tst_qloggingregistry
+
+CONFIG += testcase
+QT = core core-private testlib
+
+SOURCES += tst_qloggingregistry.cpp
+OTHER_FILES += qtlogging.ini
diff --git a/tests/auto/corelib/io/qloggingregistry/qtlogging.ini b/tests/auto/corelib/io/qloggingregistry/qtlogging.ini
new file mode 100644
index 0000000000..63b384e36a
--- /dev/null
+++ b/tests/auto/corelib/io/qloggingregistry/qtlogging.ini
@@ -0,0 +1,2 @@
+[rules]
+*=true
diff --git a/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp b/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp
new file mode 100644
index 0000000000..b538525161
--- /dev/null
+++ b/tests/auto/corelib/io/qloggingregistry/tst_qloggingregistry.cpp
@@ -0,0 +1,179 @@
+/****************************************************************************
+**
+** 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.
+**
+** $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 <QtTest>
+#include <QLoggingCategory>
+
+#include <QtCore/private/qloggingregistry_p.h>
+
+QT_USE_NAMESPACE
+
+class tst_QLoggingRegistry : public QObject
+{
+ Q_OBJECT
+
+private slots:
+
+ void initTestCase()
+ {
+ // ensure a clean environment
+ QStandardPaths::setTestModeEnabled(true);
+ qunsetenv("QT_LOGGING_CONF");
+ }
+
+ void QLoggingSettingsParser_iniStyle()
+ {
+ //
+ // Logging configuration can be described
+ // in an .ini file. [rules] is the
+ // default category, and optional ...
+ //
+ QLoggingSettingsParser parser;
+ parser.setContent("[rules]\n"
+ "default=false\n"
+ "default=true");
+ QCOMPARE(parser.rules().size(), 2);
+
+ parser.setContent("[rules]\n"
+ "default=false");
+ QCOMPARE(parser.rules().size(), 1);
+
+ parser.setContent("[OtherSection]\n"
+ "default=false");
+ QCOMPARE(parser.rules().size(), 0);
+ }
+
+ void QLoggingRegistry_environment()
+ {
+ //
+ // Check whether QT_LOGGING_CONF is picked up from environment
+ //
+
+ qputenv("QT_LOGGING_CONF", QFINDTESTDATA("qtlogging.ini").toLocal8Bit());
+
+ QLoggingRegistry registry;
+ registry.init();
+
+ QCOMPARE(registry.apiRules.size(), 0);
+ QCOMPARE(registry.configRules.size(), 0);
+ QCOMPARE(registry.envRules.size(), 1);
+
+ QCOMPARE(registry.rules.size(), 1);
+ }
+
+ void QLoggingRegistry_config()
+ {
+ //
+ // Check whether QtProject/qtlogging.ini is loaded automatically
+ //
+
+ // first try to create a test file..
+ QString path = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
+ QVERIFY(!path.isEmpty());
+ QDir dir(path + "/QtProject");
+ if (!dir.exists())
+ QVERIFY(dir.mkpath(path + "/QtProject"));
+
+ QFile file(dir.absoluteFilePath("qtlogging.ini"));
+ QVERIFY(file.open(QFile::WriteOnly | QFile::Text));
+ QTextStream out(&file);
+ out << "[rules]\n";
+ out << "Digia.*=false\n";
+ file.close();
+
+ QLoggingRegistry registry;
+ registry.init();
+ QCOMPARE(registry.configRules.size(), 1);
+
+ // remove file again
+ QVERIFY(file.remove());
+ }
+
+ void QLoggingRegistry_rulePriorities()
+ {
+ //
+ // Rules can stem from 3 sources:
+ // via QLoggingCategory::setFilterRules (API)
+ // via qtlogging.ini file in settings (Config)
+ // via QT_LOGGING_CONF environment variable (Env)
+ //
+ // Rules set by environment should get higher precedence than qtlogging.conf,
+ // than QLoggingCategory::setFilterRules
+ //
+
+ QLoggingCategory cat("Digia.Berlin");
+ QLoggingRegistry *registry = QLoggingRegistry::instance();
+
+ // empty all rules , check default
+ registry->rules.clear();
+ registry->apiRules.clear();
+ registry->configRules.clear();
+ registry->envRules.clear();
+ registry->updateRules();
+
+ QVERIFY(cat.isWarningEnabled());
+
+ // set Config rule
+ QLoggingSettingsParser parser;
+ parser.setContent("[rules]\nDigia.*=false");
+ registry->configRules=parser.rules();
+ registry->updateRules();
+
+ QVERIFY(!cat.isWarningEnabled());
+
+ // set API rule, should overwrite API one
+ QLoggingCategory::setFilterRules("Digia.*=true");
+
+ QVERIFY(cat.isWarningEnabled());
+
+ // set Env rule, should overwrite Config one
+ parser.setContent("Digia.*=false");
+ registry->envRules=parser.rules();
+ registry->updateRules();
+
+ QVERIFY(!cat.isWarningEnabled());
+ }
+
+};
+
+QTEST_MAIN(tst_QLoggingRegistry)
+
+#include "tst_qloggingregistry.moc"