diff --git a/libnymea-core/jsonrpc/logginghandler.cpp b/libnymea-core/jsonrpc/logginghandler.cpp index cf917b89..8e8a59d0 100644 --- a/libnymea-core/jsonrpc/logginghandler.cpp +++ b/libnymea-core/jsonrpc/logginghandler.cpp @@ -140,11 +140,11 @@ JsonReply* LoggingHandler::GetLogEntries(const QVariantMap ¶ms) const { LogFilter filter = unpackLogFilter(params); - LogEngineFetchJob *job = NymeaCore::instance()->logEngine()->logEntries(filter); + LogEntriesFetchJob *job = NymeaCore::instance()->logEngine()->fetchLogEntries(filter); JsonReply *reply = createAsyncReply("GetLogEntries"); - connect(job, &LogEngineFetchJob::finished, reply, [this, reply, job, filter](){ + connect(job, &LogEntriesFetchJob::finished, reply, [reply, job, filter](){ QVariantList entries; foreach (const LogEntry &entry, job->results()) { diff --git a/libnymea-core/logging/logengine.cpp b/libnymea-core/logging/logengine.cpp index fd02919d..f8e73b98 100644 --- a/libnymea-core/logging/logengine.cpp +++ b/libnymea-core/logging/logengine.cpp @@ -180,23 +180,19 @@ LogEngine::LogEngine(const QString &driver, const QString &dbName, const QString /*! Destructs the \l{LogEngine}. */ LogEngine::~LogEngine() { - qWarning() << "Destroying logEngine"; // Process the job queue before allowing to shut down while (m_currentJob) { - qWarning() << "Waiting for job to finish... (" << m_jobQueue.count() << "jobs left in queue)"; + qCDebug(dcLogEngine()) << "Waiting for job to finish... (" << m_jobQueue.count() << "jobs left in queue)"; m_jobWatcher.waitForFinished(); + // Make sure that the job queue is processes + // We can't call processQueue ourselves because thread synchronisation is done via queued connections qApp->processEvents(); } - qWarning() << "Done waiting"; qCDebug(dcLogEngine()) << "Closing Database"; m_db.close(); } -/*! Returns the list of \l{LogEntry}{LogEntries} of the database matching the given \a filter. - - \sa LogEntry, LogFilter -*/ -LogEngineFetchJob* LogEngine::logEntries(const LogFilter &filter) +LogEntriesFetchJob *LogEngine::fetchLogEntries(const LogFilter &filter) { QList results; QSqlQuery query(m_db); @@ -224,7 +220,7 @@ LogEngineFetchJob* LogEngine::logEntries(const LogFilter &filter) } DatabaseJob *job = new DatabaseJob(query); - LogEngineFetchJob *fetchJob = new LogEngineFetchJob(this); + LogEntriesFetchJob *fetchJob = new LogEntriesFetchJob(this); connect(job, &DatabaseJob::finished, this, [this, job, fetchJob](){ fetchJob->deleteLater(); @@ -246,7 +242,7 @@ LogEngineFetchJob* LogEngine::logEntries(const LogFilter &filter) entry.setEventType((Logging::LoggingEventType)job->query().value("loggingEventType").toInt()); entry.setActive(job->query().value("active").toBool()); - fetchJob->addResult(entry); + fetchJob->m_results.append(entry); } qCDebug(dcLogEngine) << "Fetched" << fetchJob->results().count() << "entries for db query:" << job->query().executedQuery(); fetchJob->finished(); @@ -257,6 +253,32 @@ LogEngineFetchJob* LogEngine::logEntries(const LogFilter &filter) return fetchJob; } +DevicesFetchJob *LogEngine::fetchDevices() +{ + QString queryString = QString("SELECT deviceId FROM entries WHERE deviceId != \"%1\" GROUP BY deviceId;").arg(QUuid().toString()); + + DatabaseJob *job = new DatabaseJob(queryString, m_db); + DevicesFetchJob *fetchJob = new DevicesFetchJob(this); + connect(job, &DatabaseJob::finished, this, [this, job, fetchJob](){ + fetchJob->deleteLater(); + if (job->query().lastError().type() != QSqlError::NoError) { + qCWarning(dcLogEngine()) << "Error fetching device entries from log database:" << m_db.lastError().driverText() << m_db.lastError().databaseText(); + fetchJob->finished(); + return; + } + + if (!job->query().first()) { + fetchJob->finished(); + return; + } + do { + fetchJob->m_results.append(DeviceId::fromUuid(job->query().value("deviceId").toUuid())); + } while (job->query().next()); + fetchJob->finished(); + }); + return fetchJob; +} + void LogEngine::setMaxLogEntries(int maxLogEntries, int overflow) { m_dbMaxSize = maxLogEntries; diff --git a/libnymea-core/logging/logengine.h b/libnymea-core/logging/logengine.h index f3f9e02d..38c84281 100644 --- a/libnymea-core/logging/logengine.h +++ b/libnymea-core/logging/logengine.h @@ -39,7 +39,8 @@ namespace nymeaserver { class DatabaseJob; -class LogEngineFetchJob; +class LogEntriesFetchJob; +class DevicesFetchJob; class LogEngine: public QObject { @@ -48,7 +49,8 @@ public: LogEngine(const QString &driver, const QString &dbName, const QString &hostname = QString("127.0.0.1"), const QString &username = QString(), const QString &password = QString(), int maxDBSize = 50000, QObject *parent = nullptr); ~LogEngine(); - LogEngineFetchJob *logEntries(const LogFilter &filter = LogFilter()); + LogEntriesFetchJob *fetchLogEntries(const LogFilter &filter = LogFilter()); + DevicesFetchJob *fetchDevices(); void setMaxLogEntries(int maxLogEntries, int overflow); void clearDatabase(); @@ -122,24 +124,32 @@ private: QSqlQuery m_query; }; -class LogEngineFetchJob: public QObject +class LogEntriesFetchJob: public QObject { Q_OBJECT public: - LogEngineFetchJob(QObject *parent): QObject(parent) {} - + LogEntriesFetchJob(QObject *parent): QObject(parent) {} QList results() { return m_results; } - signals: void finished(); - private: - void addResult(const LogEntry &entry) { m_results.append(entry); } QList m_results; - friend class LogEngine; - }; + +class DevicesFetchJob: public QObject +{ + Q_OBJECT +public: + DevicesFetchJob(QObject *parent): QObject(parent) {} + QList results() { return m_results; } +signals: + void finished(); +private: + QList m_results; + friend class LogEngine; +}; + } #endif diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp index 898f0f38..6c98bf9c 100644 --- a/libnymea-core/nymeacore.cpp +++ b/libnymea-core/nymeacore.cpp @@ -920,6 +920,29 @@ void NymeaCore::deviceManagerLoaded() onDateTimeChanged(m_timeManager->currentDateTime()); emit initialized(); + + // Do some houskeeping... + qCDebug(dcApplication()) << "Starting housekeeping..."; + QDateTime startTime = QDateTime::currentDateTime(); + DevicesFetchJob *job = m_logger->fetchDevices(); + connect(job, &DevicesFetchJob::finished, this, [this, job, startTime](){ + foreach (const DeviceId &deviceId, job->results()) { + if (!m_deviceManager->findConfiguredDevice(deviceId)) { + qCDebug(dcApplication()) << "Cleaning stale device entries from log DB for device id" << deviceId; + m_logger->removeDeviceLogs(deviceId); + } + } + qCDebug(dcApplication()) << "Housekeeping done in" << startTime.msecsTo(QDateTime::currentDateTime()) << "ms."; + }); + + foreach (const DeviceId &deviceId, m_ruleEngine->devicesInRules()) { + if (!m_deviceManager->findConfiguredDevice(deviceId)) { + qCDebug(dcApplication()) << "Cleaning stale rule entries for device id" << deviceId; + foreach (const RuleId &ruleId, m_ruleEngine->findRules(deviceId)) { + m_ruleEngine->removeDeviceFromRule(ruleId, deviceId); + } + } + } } } diff --git a/tests/auto/jsonrpc/testjsonrpc.cpp b/tests/auto/jsonrpc/testjsonrpc.cpp index e1c69d14..5da756d5 100644 --- a/tests/auto/jsonrpc/testjsonrpc.cpp +++ b/tests/auto/jsonrpc/testjsonrpc.cpp @@ -991,18 +991,16 @@ void TestJSONRPC::stateChangeEmitsNotifications() break; } } - if (!found) - qDebug() << QJsonDocument::fromVariant(stateChangedVariants).toJson(); QVERIFY2(found, "Could not find the correct Devices.StateChanged notification"); + clientSpy.wait(); // Make sure the logg notification contains all the stuff we expect - QVariantList loggEntryAddedVariants = checkNotifications(clientSpy, "Logging.LogEntryAdded"); - QVERIFY2(!loggEntryAddedVariants.isEmpty(), "Did not get Logging.LogEntryAdded notification."); - qDebug() << "got" << loggEntryAddedVariants.count() << "Logging.LogEntryAdded notifications"; + QVariantList logEntryAddedVariants = checkNotifications(clientSpy, "Logging.LogEntryAdded"); + QVERIFY2(!logEntryAddedVariants.isEmpty(), "Did not get Logging.LogEntryAdded notification."); found = false; - foreach (const QVariant &loggEntryAddedVariant, loggEntryAddedVariants) { + foreach (const QVariant &loggEntryAddedVariant, logEntryAddedVariants) { if (loggEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap().value("typeId").toUuid() == stateTypeId) { found = true; QCOMPARE(loggEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap().value("source").toString(), QString("LoggingSourceStates")); @@ -1010,8 +1008,6 @@ void TestJSONRPC::stateChangeEmitsNotifications() break; } } - if (!found) - qDebug() << QJsonDocument::fromVariant(loggEntryAddedVariants).toJson(); QVERIFY2(found, "Could not find the corresponding Logging.LogEntryAdded notification"); @@ -1020,7 +1016,6 @@ void TestJSONRPC::stateChangeEmitsNotifications() QVariantList eventTriggeredVariants = checkNotifications(clientSpy, "Events.EventTriggered"); QVERIFY2(!eventTriggeredVariants.isEmpty(), "Did not get Events.EventTriggered notification."); found = false; - qDebug() << "got" << eventTriggeredVariants.count() << "Events.EventTriggered notifications"; foreach (const QVariant &eventTriggeredVariant, eventTriggeredVariants) { if (eventTriggeredVariant.toMap().value("params").toMap().value("event").toMap().value("eventTypeId").toUuid() == stateTypeId) { found = true; @@ -1028,8 +1023,6 @@ void TestJSONRPC::stateChangeEmitsNotifications() break; } } - if (!found) - qDebug() << QJsonDocument::fromVariant(eventTriggeredVariants).toJson(); QVERIFY2(found, "Could not find the corresponding Events.EventTriggered notification"); @@ -1108,6 +1101,7 @@ void TestJSONRPC::testPushButtonAuth() void TestJSONRPC::testPushButtonAuthInterrupt() { + enableNotifications({}); PushButtonAgent pushButtonAgent; pushButtonAgent.init(); diff --git a/tests/auto/loggingdirect/testloggingdirect.cpp b/tests/auto/loggingdirect/testloggingdirect.cpp index b5d2e4de..a1eb74f9 100644 --- a/tests/auto/loggingdirect/testloggingdirect.cpp +++ b/tests/auto/loggingdirect/testloggingdirect.cpp @@ -82,8 +82,8 @@ void TestLoggingDirect::benchmarkDB() engine->setMaxLogEntries(prefill, overflow); engine->setMaxLogEntries(maxSize, overflow); - LogEngineFetchJob *job = engine->logEntries(); - QSignalSpy fetchSpy(job, &LogEngineFetchJob::finished); + LogEntriesFetchJob *job = engine->fetchLogEntries(); + QSignalSpy fetchSpy(job, &LogEntriesFetchJob::finished); fetchSpy.wait(); QList entries = job->results(); @@ -93,8 +93,8 @@ void TestLoggingDirect::benchmarkDB() engine->logSystemEvent(QDateTime::currentDateTime(), true); } - job = engine->logEntries(); - QSignalSpy fetchSpy2(job, &LogEngineFetchJob::finished); + job = engine->fetchLogEntries(); + QSignalSpy fetchSpy2(job, &LogEntriesFetchJob::finished); fetchSpy2.wait(); entries = job->results(); @@ -106,8 +106,8 @@ void TestLoggingDirect::benchmarkDB() } QDateTime now = QDateTime::currentDateTime(); - job = engine->logEntries(); - QSignalSpy fetchSpy3(job, &LogEngineFetchJob::finished); + job = engine->fetchLogEntries(); + QSignalSpy fetchSpy3(job, &LogEntriesFetchJob::finished); fetchSpy3.wait(); entries = job->results(); diff --git a/tests/auto/rules/testrules.cpp b/tests/auto/rules/testrules.cpp index b55e1a7a..e390457e 100644 --- a/tests/auto/rules/testrules.cpp +++ b/tests/auto/rules/testrules.cpp @@ -2313,8 +2313,8 @@ void TestRules::testStateBasedAction() filter.addDeviceId(m_mockDeviceId); filter.addTypeId(mockWithParamsActionTypeId); - LogEngineFetchJob *job = NymeaCore::instance()->logEngine()->logEntries(filter); - QSignalSpy fetchSpy(job, &LogEngineFetchJob::finished); + LogEntriesFetchJob *job = NymeaCore::instance()->logEngine()->fetchLogEntries(filter); + QSignalSpy fetchSpy(job, &LogEntriesFetchJob::finished); fetchSpy.wait(); QList entries = job->results(); qCDebug(dcTests()) << "Log entries:" << entries; @@ -2335,8 +2335,8 @@ void TestRules::testStateBasedAction() QCOMPARE(spy.count(), 1); reply->deleteLater(); - job = NymeaCore::instance()->logEngine()->logEntries(filter); - QSignalSpy fetchSpy2(job, &LogEngineFetchJob::finished); + job = NymeaCore::instance()->logEngine()->fetchLogEntries(filter); + QSignalSpy fetchSpy2(job, &LogEntriesFetchJob::finished); fetchSpy2.wait(); entries = job->results();