diff --git a/libnymea-core/debugserverhandler.cpp b/libnymea-core/debugserverhandler.cpp index 69d92c80..30f49e8e 100644 --- a/libnymea-core/debugserverhandler.cpp +++ b/libnymea-core/debugserverhandler.cpp @@ -1777,21 +1777,21 @@ QByteArray DebugServerHandler::createDebugXmlDocument() }; foreach (const QString &loggingCategory, loggingCategories) { writer.writeStartElement("div"); - writer.writeAttribute("class", "debug-category"); - writer.writeTextElement("p", loggingCategory); - writer.writeStartElement("label"); - writer.writeStartElement("select"); - writer.writeAttribute("class", "debug-select"); - writer.writeAttribute("onchange", QString("toggleLoggingCategory('%1', this)").arg(loggingCategory)); - writer.writeAttribute("id", QString("debug-category-%1").arg(loggingCategory)); - foreach (const QString &option, QStringList({"debug", "info", "warning", "critical"})) { - writer.writeStartElement("option"); - writer.writeAttribute("value", option); - writer.writeCharacters(categoryMap.value(option)); - writer.writeEndElement(); - } - writer.writeEndElement(); // select - writer.writeEndElement(); // label + writer.writeAttribute("class", "debug-category"); + writer.writeTextElement("p", loggingCategory); + writer.writeStartElement("label"); + writer.writeStartElement("select"); + writer.writeAttribute("class", "debug-select"); + writer.writeAttribute("onchange", QString("toggleLoggingCategory('%1', this)").arg(loggingCategory)); + writer.writeAttribute("id", QString("debug-category-%1").arg(loggingCategory)); + foreach (const QString &option, QStringList({"debug", "info", "warning", "critical"})) { + writer.writeStartElement("option"); + writer.writeAttribute("value", option); + writer.writeCharacters(categoryMap.value(option)); + writer.writeEndElement(); + } + writer.writeEndElement(); // select + writer.writeEndElement(); // label writer.writeEndElement(); // div debug-category } @@ -1813,15 +1813,15 @@ QByteArray DebugServerHandler::createDebugXmlDocument() writer.writeTextElement("p", loggingCategory); writer.writeStartElement("label"); writer.writeStartElement("select"); - writer.writeAttribute("class", "debug-select"); - writer.writeAttribute("onchange", QString("toggleLoggingCategory('%1', this)").arg(loggingCategory)); - writer.writeAttribute("id", QString("debug-category-%1").arg(loggingCategory)); - foreach (const QString &option, QStringList({"debug", "info", "warning", "critical"})) { - writer.writeStartElement("option"); - writer.writeAttribute("value", option); - writer.writeCharacters(categoryMap.value(option)); - writer.writeEndElement(); - } + writer.writeAttribute("class", "debug-select"); + writer.writeAttribute("onchange", QString("toggleLoggingCategory('%1', this)").arg(loggingCategory)); + writer.writeAttribute("id", QString("debug-category-%1").arg(loggingCategory)); + foreach (const QString &option, QStringList({"debug", "info", "warning", "critical"})) { + writer.writeStartElement("option"); + writer.writeAttribute("value", option); + writer.writeCharacters(categoryMap.value(option)); + writer.writeEndElement(); + } writer.writeEndElement(); // select writer.writeEndElement(); // label writer.writeEndElement(); // div debug-category diff --git a/libnymea-core/jsonrpc/debughandler.cpp b/libnymea-core/jsonrpc/debughandler.cpp new file mode 100644 index 00000000..4fee0e90 --- /dev/null +++ b/libnymea-core/jsonrpc/debughandler.cpp @@ -0,0 +1,221 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2024, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project is distributed in the hope that it +* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +* Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "debughandler.h" + +#include "nymeacore.h" +#include "nymeasettings.h" +#include "loggingcategories.h" + +namespace nymeaserver { + +DebugHandler::DebugHandler(QObject *parent) + : JsonHandler{parent} +{ + // Types + registerEnum(); + registerEnum(); + registerEnum(); + + QVariantMap loggingCategory; + loggingCategory.insert("name", enumValueName(String)); + loggingCategory.insert("level", enumRef()); + loggingCategory.insert("type", enumRef()); + registerObject("LoggingCategory", loggingCategory); + + QVariantMap params, returns; + QString description; + + // Methods + params.clear(); returns.clear(); + description = "Get all available logging categories."; + returns.insert("loggingCategories", QVariantList() << objectRef("LoggingCategory")); + registerMethod("GetLoggingCategories", description, params, returns); + + params.clear(); returns.clear(); + description = "Set the logging category with the given name to the given logging level."; + params.insert("name", enumValueName(String)); + params.insert("level", enumRef()); + returns.insert("debugError", enumRef()); + registerMethod("SetLoggingCategoryLevel", description, params, returns); + + // Notifications + params.clear(); returns.clear(); + description = "Emitted whenever a logging category has changed the logging level."; + params.insert("name", enumValueName(String)); + params.insert("level", enumRef()); + registerNotification("LoggingCategoryLevelChanged", description, params); +} + +QString DebugHandler::name() const +{ + return "Debug"; +} + +JsonReply *DebugHandler::GetLoggingCategories(const QVariantMap ¶ms) +{ + Q_UNUSED(params) + + QVariantList categories; + QStringList allCategories; + + NymeaSettings settings(NymeaSettings::SettingsRoleGlobal); + settings.beginGroup("LoggingRules"); + + // System + foreach (const QString &loggingCategory, NymeaCore::loggingFilters()) { + allCategories.append(loggingCategory); + + LoggingLevel level = DebugHandler::LoggingLevelCritical; + if (settings.value(QString("%1.warning").arg(loggingCategory), true).toBool()) { + level = DebugHandler::LoggingLevelWarning; + } + if (settings.value(QString("%1.info").arg(loggingCategory), false).toBool()) { + level = DebugHandler::LoggingLevelInfo; + } + if (settings.value(QString("%1.debug").arg(loggingCategory), false).toBool()) { + level = DebugHandler::LoggingLevelDebug; + } + + QVariantMap category; + category.insert("name", loggingCategory); + category.insert("level", enumValueName(level)); + category.insert("type", enumValueName(LoggingCategoryTypeSystem)); + categories.append(category); + } + + // Plugins + foreach (const QString &loggingCategory, NymeaCore::loggingFiltersPlugins()) { + allCategories.append(loggingCategory); + + LoggingLevel level = DebugHandler::LoggingLevelCritical; + if (settings.value(QString("%1.warning").arg(loggingCategory), true).toBool()) { + level = DebugHandler::LoggingLevelWarning; + } + if (settings.value(QString("%1.info").arg(loggingCategory), false).toBool()) { + level = DebugHandler::LoggingLevelInfo; + } + if (settings.value(QString("%1.debug").arg(loggingCategory), false).toBool()) { + level = DebugHandler::LoggingLevelDebug; + } + + QVariantMap category; + category.insert("name", loggingCategory); + category.insert("level", enumValueName(level)); + category.insert("type", enumValueName(LoggingCategoryTypePlugin)); + categories.append(category); + } + + // Now create all categories, which are not nymea system related + foreach (const QString &categoryFilter, settings.childGroups()) { + QStringList categoryParts = categoryFilter.split("."); + if (categoryParts.isEmpty()) + continue; + + QString loggingCategory = categoryParts.first(); + if (allCategories.contains(loggingCategory) || loggingCategory.isEmpty()) + continue; + + LoggingLevel level = DebugHandler::LoggingLevelCritical; + if (settings.value(QString("%1.warning").arg(loggingCategory), true).toBool()) { + level = DebugHandler::LoggingLevelWarning; + } + if (settings.value(QString("%1.info").arg(loggingCategory), false).toBool()) { + level = DebugHandler::LoggingLevelInfo; + } + if (settings.value(QString("%1.debug").arg(loggingCategory), false).toBool()) { + level = DebugHandler::LoggingLevelDebug; + } + + QVariantMap category; + category.insert("name", loggingCategory); + category.insert("level", enumValueName(level)); + category.insert("type", enumValueName(LoggingCategoryTypeCustom)); + categories.append(category); + } + + settings.endGroup(); // LoggingRules + + QVariantMap returns; + returns.insert("loggingCategories", categories); + return createReply(returns); +} + +JsonReply *DebugHandler::SetLoggingCategoryLevel(const QVariantMap ¶ms) +{ + QString category = params.value("name").toString(); + LoggingLevel level = enumNameToValue(params.value("level").toString()); + + NymeaSettings settings(NymeaSettings::SettingsRoleGlobal); + settings.beginGroup("LoggingRules"); + + qCDebug(dcDebugServer()) << "Logging category" << category << level; + + switch (level) { + case LoggingLevelCritical: + settings.setValue(QString("%1.warning").arg(category), false); + settings.setValue(QString("%1.info").arg(category), false); + settings.setValue(QString("%1.debug").arg(category), false); + break; + case LoggingLevelWarning: + settings.setValue(QString("%1.warning").arg(category), true); + settings.setValue(QString("%1.info").arg(category), false); + settings.setValue(QString("%1.debug").arg(category), false); + break; + case LoggingLevelInfo: + settings.setValue(QString("%1.warning").arg(category), true); + settings.setValue(QString("%1.info").arg(category), true); + settings.setValue(QString("%1.debug").arg(category), false); + break; + case LoggingLevelDebug: + settings.setValue(QString("%1.warning").arg(category), true); + settings.setValue(QString("%1.info").arg(category), true); + settings.setValue(QString("%1.debug").arg(category), true); + break; + } + + // Update logging filter rules according to the nw settings + QStringList loggingRules; + loggingRules << "*.debug=false"; + // Load the rules from nymead.conf file and append them to the rules + foreach (const QString &category, settings.childKeys()) { + loggingRules << QString("%1=%2").arg(category).arg(settings.value(category, "false").toString()); + } + settings.endGroup(); + QLoggingCategory::setFilterRules(loggingRules.join('\n')); + + emit LoggingCategoryLevelChanged(params); + + QVariantMap returns; + returns.insert("debugError", enumValueName(DebugErrorNoError)); + return createReply(returns); + } + +} diff --git a/libnymea-core/jsonrpc/debughandler.h b/libnymea-core/jsonrpc/debughandler.h new file mode 100644 index 00000000..1cd5165f --- /dev/null +++ b/libnymea-core/jsonrpc/debughandler.h @@ -0,0 +1,79 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2024, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project is distributed in the hope that it +* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +* Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef DEBUGHANDLER_H +#define DEBUGHANDLER_H + +#include + +#include "jsonrpc/jsonhandler.h" + +namespace nymeaserver { + +class DebugHandler : public JsonHandler +{ + Q_OBJECT +public: + enum DebugError { + DebugErrorNoError + }; + Q_ENUM(DebugError) + + enum LoggingCategoryType { + LoggingCategoryTypeSystem, + LoggingCategoryTypePlugin, + LoggingCategoryTypeCustom + }; + Q_ENUM(LoggingCategoryType) + + enum LoggingLevel { + LoggingLevelCritical, + LoggingLevelWarning, + LoggingLevelInfo, + LoggingLevelDebug + }; + Q_ENUM(LoggingLevel) + + explicit DebugHandler(QObject *parent = nullptr); + + QString name() const override; + +public slots: + JsonReply* GetLoggingCategories(const QVariantMap ¶ms); + JsonReply* SetLoggingCategoryLevel(const QVariantMap ¶ms); + +signals: + void LoggingCategoryLevelChanged(const QVariantMap ¶ms); + +}; + +} + +#endif // DEBUGHANDLER_H diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro index 98253acc..a058dca2 100644 --- a/libnymea-core/libnymea-core.pro +++ b/libnymea-core/libnymea-core.pro @@ -63,6 +63,7 @@ HEADERS += nymeacore.h \ hardware/network/macaddressdatabasereplyimpl.h \ hardware/serialport/serialportmonitor.h \ hardware/zwave/zwavehardwareresourceimplementation.h \ + jsonrpc/debughandler.h \ logging/logengineinfluxdb.h \ scriptengine/scriptthing.h \ scriptengine/scriptthings.h \ @@ -172,6 +173,7 @@ SOURCES += nymeacore.cpp \ hardware/network/macaddressdatabasereplyimpl.cpp \ hardware/serialport/serialportmonitor.cpp \ hardware/zwave/zwavehardwareresourceimplementation.cpp \ + jsonrpc/debughandler.cpp \ logging/logengineinfluxdb.cpp \ scriptengine/scriptthing.cpp \ scriptengine/scriptthings.cpp \ diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp index 4b1f5c1b..c80c3489 100644 --- a/libnymea-core/nymeacore.cpp +++ b/libnymea-core/nymeacore.cpp @@ -41,6 +41,7 @@ #include "logging/logengineinfluxdb.h" #include "scriptengine/scriptengine.h" #include "jsonrpc/scriptshandler.h" +#include "jsonrpc/debughandler.h" #include "version.h" #include "integrations/thingmanagerimplementation.h" @@ -153,10 +154,12 @@ void NymeaCore::init(const QStringList &additionalInterfaces, bool disableLogEng qCDebug(dcCore) << "Creating Debug Server Handler"; m_debugServerHandler = new DebugServerHandler(this); + qCDebug(dcCore) << "Register Debug Handler"; + m_serverManager->jsonServer()->registerHandler(new DebugHandler(m_serverManager->jsonServer())); + qCDebug(dcCore()) << "Loading experiences"; m_experienceManager = new ExperienceManager(m_thingManager, m_serverManager->jsonServer(), this); - connect(m_configuration, &NymeaConfiguration::serverNameChanged, m_serverManager, &ServerManager::setServerName); connect(m_thingManager, &ThingManagerImplementation::loaded, this, &NymeaCore::thingManagerLoaded); diff --git a/libnymea/loggingcategories.h b/libnymea/loggingcategories.h index 9f8f20d7..1ac57c5e 100644 --- a/libnymea/loggingcategories.h +++ b/libnymea/loggingcategories.h @@ -39,7 +39,11 @@ QStringList& nymeaLoggingCategories(); #define NYMEA_LOGGING_CATEGORY(name, string) \ class NymeaLoggingCategory##name: public QLoggingCategory { \ public: \ - NymeaLoggingCategory##name(): QLoggingCategory(string) { nymeaLoggingCategories().append(string); } \ + NymeaLoggingCategory##name(): QLoggingCategory(string) { \ + if (!nymeaLoggingCategories().contains(string)) { \ + nymeaLoggingCategories().append(string); \ + } \ + } \ }; \ static NymeaLoggingCategory##name s_##name; \ const QLoggingCategory &name() \ @@ -47,6 +51,7 @@ QStringList& nymeaLoggingCategories(); return s_##name; \ } \ + // FIXME: Those should eventually disappear from here Q_DECLARE_LOGGING_CATEGORY(dcThing) Q_DECLARE_LOGGING_CATEGORY(dcThingManager) diff --git a/nymea.pro b/nymea.pro index bb1d03bf..481c232e 100644 --- a/nymea.pro +++ b/nymea.pro @@ -11,7 +11,7 @@ isEmpty(NYMEA_VERSION) { # define protocol versions JSON_PROTOCOL_VERSION_MAJOR=8 -JSON_PROTOCOL_VERSION_MINOR=1 +JSON_PROTOCOL_VERSION_MINOR=2 JSON_PROTOCOL_VERSION="$${JSON_PROTOCOL_VERSION_MAJOR}.$${JSON_PROTOCOL_VERSION_MINOR}" LIBNYMEA_API_VERSION_MAJOR=8 LIBNYMEA_API_VERSION_MINOR=0 diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 70e8a865..18c89b12 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -2,6 +2,7 @@ TEMPLATE = subdirs SUBDIRS = \ configurations \ + debughandler \ integrations \ ioconnections \ jsonrpc \ diff --git a/tests/auto/debughandler/debughandler.pro b/tests/auto/debughandler/debughandler.pro new file mode 100644 index 00000000..48961170 --- /dev/null +++ b/tests/auto/debughandler/debughandler.pro @@ -0,0 +1,7 @@ +TARGET = nymeatestdebughandler + +include(../../../nymea.pri) +include(../autotests.pri) + +SOURCES += testdebughandler.cpp + diff --git a/tests/auto/debughandler/testdebughandler.cpp b/tests/auto/debughandler/testdebughandler.cpp new file mode 100644 index 00000000..e8ea7d25 --- /dev/null +++ b/tests/auto/debughandler/testdebughandler.cpp @@ -0,0 +1,70 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2024, nymea GmbH +* Contact: contact@nymea.io +* +* This file is part of nymea. +* This project including source code and documentation is protected by +* copyright law, and remains the property of nymea GmbH. All rights, including +* reproduction, publication, editing and translation, are reserved. The use of +* this project is subject to the terms of a license agreement to be concluded +* with nymea GmbH in accordance with the terms of use of nymea GmbH, available +* under https://nymea.io/license +* +* GNU General Public License Usage +* Alternatively, this project may be redistributed and/or modified under the +* terms of the GNU General Public License as published by the Free Software +* Foundation, GNU version 3. This project is distributed in the hope that it +* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +* Public License for more details. +* +* You should have received a copy of the GNU General Public License along with +* this project. If not, see . +* +* For any further details and any questions please contact us under +* contact@nymea.io or see our FAQ/Licensing Information on +* https://nymea.io/license/faq +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "nymeatestbase.h" + +using namespace nymeaserver; + +class TestDebugHandler: public NymeaTestBase +{ + Q_OBJECT + +protected slots: + void initTestCase(); + +private slots: + void getLoggingFilters(); + +}; + +void TestDebugHandler::initTestCase() +{ + NymeaTestBase::initTestCase("*.debug=false\nApplication.debug=true\nTests.debug=true\nServerManager.debug=true"); +} + +void TestDebugHandler::getLoggingFilters() +{ + QVariant response = injectAndWait("Debug.GetLoggingCategories"); + QVariantMap loggingFilters = response.toMap().value("params").toMap(); + + QVERIFY(loggingFilters.contains("loggingCategories")); + QVariantList loggingCategoriesList = loggingFilters.value("loggingCategories").toList(); + QVERIFY(!loggingCategoriesList.isEmpty()); + + foreach(const QVariant &categoryVariant, loggingCategoriesList) { + QVariantMap categoryMap = categoryVariant.toMap(); + QVERIFY(categoryMap.contains("level")); + QVERIFY(categoryMap.contains("name")); + QVERIFY(categoryMap.contains("type")); + } +} + +#include "testdebughandler.moc" +QTEST_MAIN(TestDebugHandler)