Added Logging Category support to QML

New Logging Categories can be defined by using the
LoggingCategory type and define a name for the category

When the id of a valid LoggingCategory is provided as
the first argument to console.log and friends the
LoggingCategory is used instead of the default "qml"
LoggingCategory

[ChangeLog][QML Elements] Added a LoggingCategory type and added support
for it to the console object

Change-Id: Ifaeed5f71de6ea6d8172d8c838d6e7789c4d6b9d
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Dominik Holland 2016-05-02 17:29:40 +02:00
parent a3aad1a470
commit eea2754976
9 changed files with 358 additions and 9 deletions

View File

@ -49,7 +49,8 @@ SOURCES += \
$$PWD/qqmlfileselector.cpp \
$$PWD/qqmlobjectcreator.cpp \
$$PWD/qqmldirparser.cpp \
$$PWD/qqmldelayedcallqueue.cpp
$$PWD/qqmldelayedcallqueue.cpp \
$$PWD/qqmlloggingcategory.cpp
HEADERS += \
$$PWD/qqmlglobal_p.h \
@ -121,7 +122,8 @@ HEADERS += \
$$PWD/qqmlfileselector.h \
$$PWD/qqmlobjectcreator_p.h \
$$PWD/qqmldirparser_p.h \
$$PWD/qqmldelayedcallqueue_p.h
$$PWD/qqmldelayedcallqueue_p.h \
$$PWD/qqmlloggingcategory_p.h
include(ftw/ftw.pri)
include(v8/v8.pri)

View File

@ -89,6 +89,7 @@
#include <private/qqmlobjectmodel_p.h>
#include <private/qquickworkerscript_p.h>
#include <private/qqmlinstantiator_p.h>
#include <private/qqmlloggingcategory_p.h>
#ifdef Q_OS_WIN // for %APPDATA%
# include <qt_windows.h>
@ -184,6 +185,7 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int
qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1
qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections", new QQmlConnectionsParser);
qmlRegisterType<QQmlInstanceModel>();
qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "LoggingCategory"); //Only available in >=2.8
}

View File

@ -0,0 +1,128 @@
/****************************************************************************
**
** Copyright (C) 2016 Pelagicore AG
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qqmlloggingcategory_p.h"
#include <QtQml/qqmlinfo.h>
/*!
\qmltype LoggingCategory
\ingroup qml-utility-elements
\inqmlmodule QtQml
\brief Defines a logging category in QML
\since 5.8
A logging category can be passed to console.log() and friends as the first argument.
If supplied to to the logger the LoggingCategory's name will be used as Logging Category
otherwise the default logging category will be used.
\qml
import QtQuick 2.8
Item {
LoggingCategory {
id: category
name: "com.qt.category"
}
Component.onCompleted: {
console.log(category, "message");
}
}
\endqml
\note As the creation of objects is expensive, it is encouraged to put the needed
LoggingCategory definitions into a singleton and import this where needed.
\sa QLoggingCategory
*/
/*!
\qmlproperty string QtQml::LoggingCategory::name
Holds the name of the logging category.
\note This property needs to be set when declaring the LoggingCategory
and cannot be changed later.
\sa QLoggingCategory::name()
*/
QQmlLoggingCategory::QQmlLoggingCategory(QObject *parent)
: QObject(parent)
, m_initialized(false)
{
}
QQmlLoggingCategory::~QQmlLoggingCategory()
{
}
QString QQmlLoggingCategory::name() const
{
return QString::fromUtf8(m_name);
}
QLoggingCategory *QQmlLoggingCategory::category() const
{
return m_category.data();
}
void QQmlLoggingCategory::classBegin()
{
}
void QQmlLoggingCategory::componentComplete()
{
m_initialized = true;
if (m_name.isNull())
qmlInfo(this) << QString(QLatin1String("Declaring the name of the LoggingCategory is mandatory and cannot be changed later !"));
}
void QQmlLoggingCategory::setName(const QString &name)
{
if (m_initialized) {
qmlInfo(this) << QString(QLatin1String("The name of a LoggingCategory cannot be changed after the Item is created"));
return;
}
m_name = name.toUtf8();
QScopedPointer<QLoggingCategory> category(new QLoggingCategory(m_name.constData()));
m_category.swap(category);
}

View File

@ -0,0 +1,89 @@
/****************************************************************************
**
** Copyright (C) 2016 Pelagicore AG
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QQMLLOGGINGCATEGORY_P_H
#define QQMLLOGGINGCATEGORY_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/qobject.h>
#include <QtCore/qstring.h>
#include <QtCore/qloggingcategory.h>
#include <QtQml/qqmlparserstatus.h>
QT_BEGIN_NAMESPACE
class QQmlLoggingCategory : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QString name READ name WRITE setName)
public:
QQmlLoggingCategory(QObject *parent = 0);
virtual ~QQmlLoggingCategory();
QString name() const;
void setName(const QString &name);
QLoggingCategory *category() const;
void classBegin() override;
void componentComplete() override;
private:
QByteArray m_name;
QScopedPointer<QLoggingCategory> m_category;
bool m_initialized;
};
QT_END_NAMESPACE
#endif // QQMLLOGGINGCATEGORY_H

View File

@ -42,6 +42,7 @@
#include <QtQml/qqmlcomponent.h>
#include <private/qqmlengine_p.h>
#include <private/qqmlcomponent_p.h>
#include <private/qqmlloggingcategory_p.h>
#include <private/qqmlstringconverters_p.h>
#include <private/qqmllocale_p.h>
#include <private/qv8engine_p.h>
@ -76,6 +77,8 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qloggingcategory.h>
#include <QDebug>
QT_BEGIN_NAMESPACE
using namespace QV4;
@ -1464,11 +1467,26 @@ static QString jsStack(QV4::ExecutionEngine *engine) {
static QV4::ReturnedValue writeToConsole(ConsoleLogTypes logType, CallContext *ctx,
bool printStack = false)
{
QLoggingCategory *loggingCategory = 0;
QString result;
QV4::ExecutionEngine *v4 = ctx->d()->engine;
for (int i = 0; i < ctx->argc(); ++i) {
if (i != 0)
int start = 0;
if (ctx->argc() > 0) {
if (const QObjectWrapper* wrapper = ctx->args()[0].as<QObjectWrapper>()) {
if (QQmlLoggingCategory* category = qobject_cast<QQmlLoggingCategory*>(wrapper->object())) {
if (category->category())
loggingCategory = category->category();
else
V4THROW_ERROR("A QmlLoggingCatgory was provided without a valid name");
start = 1;
}
}
}
for (int i = start; i < ctx->argc(); ++i) {
if (i != start)
result.append(QLatin1Char(' '));
if (ctx->args()[i].as<ArrayObject>())
@ -1485,7 +1503,8 @@ static QV4::ReturnedValue writeToConsole(ConsoleLogTypes logType, CallContext *c
static QLoggingCategory qmlLoggingCategory("qml");
static QLoggingCategory jsLoggingCategory("js");
QLoggingCategory *loggingCategory = v4->qmlEngine() ? &qmlLoggingCategory : &jsLoggingCategory;
if (!loggingCategory)
loggingCategory = v4->qmlEngine() ? &qmlLoggingCategory : &jsLoggingCategory;
QV4::StackFrame frame = v4->currentStackFrame();
const QByteArray baSource = frame.source.toUtf8();
const QByteArray baFunction = frame.function.toUtf8();

View File

@ -0,0 +1,65 @@
/****************************************************************************
**
** Copyright (C) 2016 Pelagicore AG
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
import QtQuick 2.8
Item {
id:root
LoggingCategory {
id: testCategory
name: "qt.test"
}
LoggingCategory {
id: emptyCategory
}
Component.onCompleted: {
console.debug(testCategory, "console.debug");
console.log(testCategory, "console.log");
console.info(testCategory, "console.info");
console.warn(testCategory, "console.warn");
console.error(testCategory, "console.error");
testCategory.name = "qt.test2";
console.error(emptyCategory, "console.error");
}
}

View File

@ -40,6 +40,7 @@ public:
private slots:
void logging();
void categorized_logging();
void tracing();
void profiling();
void testAssert();
@ -87,6 +88,41 @@ void tst_qqmlconsole::logging()
delete object;
}
void tst_qqmlconsole::categorized_logging()
{
QUrl testUrl = testFileUrl("categorized_logging.qml");
QQmlTestMessageHandler messageHandler;
messageHandler.setIncludeCategoriesEnabled(true);
QLoggingCategory testCategory("qt.test");
testCategory.setEnabled(QtDebugMsg, true);
QVERIFY(testCategory.isDebugEnabled());
QVERIFY(testCategory.isWarningEnabled());
QVERIFY(testCategory.isCriticalEnabled());
QQmlComponent component(&engine, testUrl);
QObject *object = component.create();
QVERIFY2(object != 0, component.errorString().toUtf8());
QVERIFY(messageHandler.messages().contains("qt.test: console.info"));
QVERIFY(messageHandler.messages().contains("qt.test: console.warn"));
QVERIFY(messageHandler.messages().contains("qt.test: console.error"));
QString emptyCategory = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(50).arg(5) +
"QML LoggingCategory: Declaring the name of the LoggingCategory is mandatory and cannot be changed later !";
QVERIFY(messageHandler.messages().contains(emptyCategory));
QString changedCategory = "default: " + QString::fromLatin1("%1:%2:%3: ").arg(testUrl.toString()).arg(45).arg(5) +
"QML LoggingCategory: The name of a LoggingCategory cannot be changed after the Item is created";
QVERIFY(messageHandler.messages().contains(changedCategory));
QString useEmptyCategory = "default: " + QString::fromLatin1("%1:%2: ").arg(testUrl.toString()).arg(63) +
"Error: A QmlLoggingCatgory was provided without a valid name";
QVERIFY(messageHandler.messages().contains(useEmptyCategory));
delete object;
}
void tst_qqmlconsole::tracing()
{
QUrl testUrl = testFileUrl("tracing.qml");

View File

@ -101,11 +101,15 @@ Q_GLOBAL_STATIC(QMutex, qQmlTestMessageHandlerMutex)
QQmlTestMessageHandler *QQmlTestMessageHandler::m_instance = 0;
void QQmlTestMessageHandler::messageHandler(QtMsgType, const QMessageLogContext &, const QString &message)
void QQmlTestMessageHandler::messageHandler(QtMsgType, const QMessageLogContext &context, const QString &message)
{
QMutexLocker locker(qQmlTestMessageHandlerMutex());
if (QQmlTestMessageHandler::m_instance)
QQmlTestMessageHandler::m_instance->m_messages.push_back(message);
if (QQmlTestMessageHandler::m_instance) {
if (QQmlTestMessageHandler::m_instance->m_includeCategories)
QQmlTestMessageHandler::m_instance->m_messages.push_back(QString("%1: %2").arg(context.category, message));
else
QQmlTestMessageHandler::m_instance->m_messages.push_back(message);
}
}
QQmlTestMessageHandler::QQmlTestMessageHandler()
@ -114,6 +118,7 @@ QQmlTestMessageHandler::QQmlTestMessageHandler()
Q_ASSERT(!QQmlTestMessageHandler::m_instance);
QQmlTestMessageHandler::m_instance = this;
m_oldHandler = qInstallMessageHandler(messageHandler);
m_includeCategories = false;
}
QQmlTestMessageHandler::~QQmlTestMessageHandler()

View File

@ -87,12 +87,15 @@ public:
void clear() { m_messages.clear(); }
void setIncludeCategoriesEnabled(bool enabled) { m_includeCategories = enabled; }
private:
static void messageHandler(QtMsgType, const QMessageLogContext &, const QString &message);
static void messageHandler(QtMsgType, const QMessageLogContext &context, const QString &message);
static QQmlTestMessageHandler *m_instance;
QStringList m_messages;
QtMessageHandler m_oldHandler;
bool m_includeCategories;
};
#endif // QQMLTESTUTILS_H