diff --git a/server/jsonrpc/jsontypes.cpp b/server/jsonrpc/jsontypes.cpp index 707c988e..22354845 100644 --- a/server/jsonrpc/jsontypes.cpp +++ b/server/jsonrpc/jsontypes.cpp @@ -751,6 +751,55 @@ StateDescriptor JsonTypes::unpackStateDescriptor(const QVariantMap &stateDescrip return stateDescriptor; } +LogFilter JsonTypes::unpackLogFilter(const QVariantMap &logFilterMap) +{ + LogFilter filter; + if (logFilterMap.contains("startTime")) { + filter.setStartDate(QDateTime::fromMSecsSinceEpoch(logFilterMap.value("startTime").toInt())); + } + if (logFilterMap.contains("endTime")) { + filter.setEndDate(QDateTime::fromMSecsSinceEpoch(logFilterMap.value("endTime").toInt())); + } + if (logFilterMap.contains("loggingSources")) { + QVariantList loggingSources = logFilterMap.value("loggingSources").toList(); + foreach (const QVariant &source, loggingSources) { + filter.addLoggingSource((Logging::LoggingSource)s_loggingSource.indexOf(source.toString())); + } + } + if (logFilterMap.contains("loggingLevels")) { + QVariantList loggingLevels = logFilterMap.value("loggingLevels").toList(); + foreach (const QVariant &level, loggingLevels) { + filter.addLoggingLevel((Logging::LoggingLevel)s_loggingLevel.indexOf(level.toString())); + } + } + if (logFilterMap.contains("eventTypes")) { + QVariantList eventTypes = logFilterMap.value("eventTypes").toList(); + foreach (const QVariant &eventType, eventTypes) { + filter.addLoggingEventType((Logging::LoggingEventType)s_loggingEventType.indexOf(eventType.toString())); + } + } + if (logFilterMap.contains("typeIds")) { + QVariantList typeIds = logFilterMap.value("typeIds").toList(); + foreach (const QVariant &typeId, typeIds) { + filter.addTypeId(typeId.toUuid()); + } + } + if (logFilterMap.contains("deviceIds")) { + QVariantList deviceIds = logFilterMap.value("deviceIds").toList(); + foreach (const QVariant &deviceId, deviceIds) { + filter.addDeviceId(DeviceId(deviceId.toString())); + } + } + if (logFilterMap.contains("values")) { + QVariantList values = logFilterMap.value("values").toList(); + foreach (const QVariant &value, values) { + filter.addValue(value.toString()); + } + } + + return filter; +} + QPair JsonTypes::validateMap(const QVariantMap &templateMap, const QVariantMap &map) { s_lastError.clear(); diff --git a/server/jsonrpc/jsontypes.h b/server/jsonrpc/jsontypes.h index 24ae2f1c..39b1b78f 100644 --- a/server/jsonrpc/jsontypes.h +++ b/server/jsonrpc/jsontypes.h @@ -37,6 +37,7 @@ #include "logging/logging.h" #include "logging/logentry.h" +#include "logging/logfilter.h" #include @@ -166,6 +167,7 @@ public: static EventDescriptor unpackEventDescriptor(const QVariantMap &eventDescriptorMap); static StateEvaluator unpackStateEvaluator(const QVariantMap &stateEvaluatorMap); static StateDescriptor unpackStateDescriptor(const QVariantMap &stateDescriptorMap); + static LogFilter unpackLogFilter(const QVariantMap &logFilterMap); static QPair validateMap(const QVariantMap &templateMap, const QVariantMap &map); static QPair validateProperty(const QVariant &templateValue, const QVariant &value); diff --git a/server/jsonrpc/logginghandler.cpp b/server/jsonrpc/logginghandler.cpp index 19ada5ea..b3a0c11d 100644 --- a/server/jsonrpc/logginghandler.cpp +++ b/server/jsonrpc/logginghandler.cpp @@ -21,8 +21,9 @@ #include "logginghandler.h" #include "logging/logengine.h" -#include "guhcore.h" +#include "logging/logfilter.h" #include "loggingcategories.h" +#include "guhcore.h" namespace guhserver { @@ -40,7 +41,16 @@ LoggingHandler::LoggingHandler(QObject *parent) : params.clear(); returns.clear(); setDescription("GetLogEntries", "Get the LogEntries matching the given filter."); - // params.insert("eventTypeId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); + + params.insert("o:startTime", JsonTypes::basicTypeToString(JsonTypes::Int)); + params.insert("o:endTime", JsonTypes::basicTypeToString(JsonTypes::Int)); + params.insert("o:loggingSources", QVariantList() << JsonTypes::loggingSourceRef()); + params.insert("o:loggingLevels", QVariantList() << JsonTypes::loggingLevelRef()); + params.insert("o:eventTypes", QVariantList() << JsonTypes::loggingEventTypeRef()); + params.insert("o:typeIds", QVariantList() << JsonTypes::basicTypeToString(JsonTypes::Uuid)); + params.insert("o:deviceIds", QVariantList() << JsonTypes::basicTypeToString(JsonTypes::Uuid)); + params.insert("o:values", QVariantList() << JsonTypes::basicTypeToString(JsonTypes::Variant)); + setParams("GetLogEntries", params); returns.insert("loggingError", JsonTypes::loggingErrorRef()); returns.insert("o:logEntries", QVariantList() << JsonTypes::logEntryRef()); @@ -64,8 +74,11 @@ void LoggingHandler::logEntryAdded(const LogEntry &logEntry) JsonReply* LoggingHandler::GetLogEntries(const QVariantMap ¶ms) const { qCDebug(dcJsonRpc) << "asked for log entries" << params; + + LogFilter filter = JsonTypes::unpackLogFilter(params); + QVariantList entries; - foreach (const LogEntry &entry, GuhCore::instance()->logEngine()->logEntries()) { + foreach (const LogEntry &entry, GuhCore::instance()->logEngine()->logEntries(filter)) { entries.append(JsonTypes::packLogEntry(entry)); } QVariantMap returns = statusToReply(Logging::LoggingErrorNoError); diff --git a/server/logging/logengine.cpp b/server/logging/logengine.cpp index abe241ec..0639b897 100644 --- a/server/logging/logengine.cpp +++ b/server/logging/logengine.cpp @@ -1,6 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2014 Michael Zanetti * + * Copyright (C) 2015 Simon Stuerz * * * * This file is part of guh. * * * @@ -37,6 +38,12 @@ LogEngine::LogEngine(QObject *parent): { m_db = QSqlDatabase::addDatabase("QSQLITE"); m_db.setDatabaseName("/tmp/guhd-logs.sqlite"); + + if (!m_db.isValid()) { + qCWarning(dcLogEngine) << "Database not valid:" << m_db.lastError().driverText() << m_db.lastError().databaseText(); + return; + } + if (!m_db.open()) { qCWarning(dcLogEngine) << "Error opening log database:" << m_db.lastError().driverText() << m_db.lastError().databaseText(); return; @@ -45,25 +52,38 @@ LogEngine::LogEngine(QObject *parent): initDB(); } -QList LogEngine::logEntries() const +QList LogEngine::logEntries(const LogFilter &filter) const { + qCDebug(dcLogEngine) << "Read logging database" << m_db.databaseName(); + QList results; QSqlQuery query; - query.exec("SELECT * FROM entries;"); + + QString queryCall = "SELECT * FROM entries;"; + if (filter.isEmpty()) { + query.exec(queryCall); + } else { + queryCall = QString("SELECT * FROM entries WHERE %1;").arg(filter.queryString()); + query.exec(queryCall); + } + while (query.next()) { LogEntry entry( - QDateTime::fromMSecsSinceEpoch(query.value("timestamp").toLongLong()), - (Logging::LoggingLevel)query.value("loggingLevel").toInt(), - (Logging::LoggingSource)query.value("sourceType").toInt(), - query.value("errorCode").toInt()); + QDateTime::fromMSecsSinceEpoch(query.value("timestamp").toLongLong()), + (Logging::LoggingLevel)query.value("loggingLevel").toInt(), + (Logging::LoggingSource)query.value("sourceType").toInt(), + query.value("errorCode").toInt()); entry.setTypeId(query.value("typeId").toUuid()); entry.setDeviceId(DeviceId(query.value("deviceId").toString())); entry.setValue(query.value("value").toString()); if ((Logging::LoggingEventType)query.value("loggingEventType").toInt() == Logging::LoggingEventTypeActiveChange) { entry.setActive(query.value("active").toBool()); } + //qCDebug(dcLogEngine) << entry; results.append(entry); } + qCDebug(dcLogEngine) << "Fetched" << results.count() << "entries for db query:" << queryCall; + return results; } diff --git a/server/logging/logengine.h b/server/logging/logengine.h index a2da80e5..65ede922 100644 --- a/server/logging/logengine.h +++ b/server/logging/logengine.h @@ -1,6 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2014 Michael Zanetti * + * Copyright (C) 2015 Simon Stuerz * * * * This file is part of guh. * * * @@ -22,6 +23,7 @@ #define LOGENGINE_H #include "logentry.h" +#include "logfilter.h" #include "types/event.h" #include "types/action.h" #include "rule.h" @@ -37,7 +39,7 @@ class LogEngine: public QObject public: LogEngine(QObject *parent = 0); - QList logEntries() const; + QList logEntries(const LogFilter &filter = LogFilter()) const; signals: void logEntryAdded(const LogEntry &logEntry); diff --git a/server/logging/logentry.cpp b/server/logging/logentry.cpp index cabdeebb..e199b1da 100644 --- a/server/logging/logentry.cpp +++ b/server/logging/logentry.cpp @@ -1,6 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2014 Michael Zanetti * + * Copyright (C) 2015 Simon Stuerz * * * * This file is part of guh. * * * @@ -19,6 +20,7 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "logentry.h" +#include "jsonrpc/jsontypes.h" #include @@ -57,11 +59,41 @@ Logging::LoggingLevel LogEntry::level() const return m_level; } +QString LogEntry::levelString() const +{ + switch (m_level) { + case Logging::LoggingLevelAlert: + return "LoggingLevelAlert"; + case Logging::LoggingLevelInfo: + return "LoggingLevelInfo"; + default: + return "< Unknown >"; + } +} + Logging::LoggingSource LogEntry::source() const { return m_source; } +QString LogEntry::sourceString() const +{ + switch (m_source) { + case Logging::LoggingSourceActions: + return "LoggingSourceActions"; + case Logging::LoggingSourceEvents: + return "LoggingSourceEvents"; + case Logging::LoggingSourceRules: + return "LoggingSourceRules"; + case Logging::LoggingSourceStates: + return "LoggingSourceStates"; + case Logging::LoggingSourceSystem: + return "LoggingSourceSystem"; + default: + return "< Unknown >"; + } +} + QUuid LogEntry::typeId() const { return m_typeId; @@ -96,6 +128,18 @@ Logging::LoggingEventType LogEntry::eventType() const return m_eventType; } +QString LogEntry::eventTypeString() const +{ + switch (m_eventType) { + case Logging::LoggingEventTypeActiveChange: + return "LoggingEventTypeActiveChange"; + case Logging::LoggingEventTypeTrigger: + return "LoggingEventTypeTrigger"; + default: + return "< Unknown >"; + } +} + bool LogEntry::active() const { return m_active; @@ -112,4 +156,18 @@ int LogEntry::errorCode() const return m_errorCode; } +QDebug operator<<(QDebug dbg, const LogEntry &entry) +{ + dbg.nospace() << "LogEntry (count:" << entry.timestamp().toString() << endl; + dbg.nospace() << " DeviceId: " << entry.deviceId().toString() << endl; + dbg.nospace() << " type id: " << entry.typeId().toString() << endl; + dbg.nospace() << " source: " << entry.sourceString() << endl; + dbg.nospace() << " level: " << entry.levelString() << endl; + dbg.nospace() << " eventType: " << entry.eventTypeString() << endl; + dbg.nospace() << " error code: " << entry.errorCode() << endl; + dbg.nospace() << " active: " << entry.active() << endl; + dbg.nospace() << " value: " << entry.value() << endl; + return dbg.space(); +} + } diff --git a/server/logging/logentry.h b/server/logging/logentry.h index aaa7dbd9..fb6267f9 100644 --- a/server/logging/logentry.h +++ b/server/logging/logentry.h @@ -1,6 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2014 Michael Zanetti * + * Copyright (C) 2015 Simon Stuerz * * * * This file is part of guh. * * * @@ -41,8 +42,11 @@ public: // Valid for all LoggingSources QDateTime timestamp() const; Logging::LoggingLevel level() const; + QString levelString() const; Logging::LoggingSource source() const; + QString sourceString() const; Logging::LoggingEventType eventType() const; + QString eventTypeString() const; // Valid for LoggingSourceStates, LoggingSourceEvents, LoggingSourceActions, LoggingSourceRules QUuid typeId() const; @@ -77,6 +81,7 @@ private: bool m_active; int m_errorCode; }; +QDebug operator<<(QDebug dbg, const LogEntry &entry); } diff --git a/server/logging/logfilter.cpp b/server/logging/logfilter.cpp index e69de29b..6f28e8e8 100644 --- a/server/logging/logfilter.cpp +++ b/server/logging/logfilter.cpp @@ -0,0 +1,306 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 2015 Simon Stuerz * + * * + * This file is part of guh. * + * * + * Guh is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, version 2 of the License. * + * * + * Guh 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 guh. If not, see . * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "logfilter.h" + +namespace guhserver { + +LogFilter::LogFilter() +{ + +} + +QString LogFilter::queryString() const +{ + if (isEmpty()) { + return QString(); + } + + QString query; + query.append(createDateString()); + + if (!query.isEmpty() && !m_sources.isEmpty()) { + query.append("AND "); + } + query.append(createSourcesString()); + + if (!query.isEmpty() && !m_levels.isEmpty()) { + query.append("AND "); + } + query.append(createLevelsString()); + + if (!query.isEmpty() && !m_eventTypes.isEmpty()) { + query.append("AND "); + } + query.append(createEventTypesString()); + + if (!query.isEmpty() && !m_typeIds.isEmpty()) { + query.append("AND "); + } + query.append(createTypeIdsString()); + + if (!query.isEmpty() && !m_deviceIds.isEmpty()) { + query.append("AND "); + } + query.append(createDeviceIdString()); + + if (!query.isEmpty() && !m_values.isEmpty()) { + query.append("AND "); + } + query.append(createValuesString()); + + return query; +} + +void LogFilter::setStartDate(const QDateTime &startDate) +{ + m_startDate = startDate; +} + +QDateTime LogFilter::startDate() const +{ + return m_startDate; +} + +void LogFilter::setEndDate(const QDateTime &endDate) +{ + m_endDate = endDate; +} + +QDateTime LogFilter::endDate() const +{ + return m_endDate; +} + +void LogFilter::addLoggingSource(const Logging::LoggingSource &source) +{ + if (!m_sources.contains(source)) + m_sources.append(source); +} + +QList LogFilter::loggingSources() const +{ + return m_sources; +} + +void LogFilter::addLoggingLevel(const Logging::LoggingLevel &level) +{ + if (!m_levels.contains(level)) + m_levels.append(level); +} + +QList LogFilter::loggingLevels() const +{ + return m_levels; +} + +void LogFilter::addLoggingEventType(const Logging::LoggingEventType &eventType) +{ + if (!m_eventTypes.contains(eventType)) + m_eventTypes.append(eventType); +} + +QList LogFilter::loggingEventTypes() const +{ + return m_eventTypes; +} + +void LogFilter::addTypeId(const QUuid &typeId) +{ + if (!m_typeIds.contains(typeId)) + m_typeIds.append(typeId); +} + +QList LogFilter::typeIds() const +{ + return m_typeIds; +} + +void LogFilter::addDeviceId(const DeviceId &deviceId) +{ + if (!m_deviceIds.contains(deviceId)) + m_deviceIds.append(deviceId); +} + +QList LogFilter::deviceIds() const +{ + return m_deviceIds; +} + +void LogFilter::addValue(const QString &value) +{ + if (!m_values.contains(value)) + m_values.append(value); +} + +QList LogFilter::values() const +{ + return m_values; +} + +bool LogFilter::isEmpty() const +{ + return m_endDate.isNull() && + m_startDate.isNull() && + m_sources.isEmpty() && + m_levels.isEmpty() && + m_eventTypes.isEmpty() && + m_typeIds.isEmpty() && + m_deviceIds.isEmpty() && + m_values.isEmpty(); +} + +QString LogFilter::createDateString() const +{ + QString query; + if (m_startDate.isValid() && !m_endDate.isValid()) { + // only start date is valid + query.append(QString("timestamp BETWEEN '%1' AND '%2' ") + .arg(m_startDate.toMSecsSinceEpoch()) + .arg(QDateTime::currentDateTime().toMSecsSinceEpoch())); + } else if (!m_startDate.isValid() && m_endDate.isValid()) { + // only end date is valid + query.append(QString("timestamp NOT BETWEEN '%1' AND '%2' ") + .arg(m_endDate.toMSecsSinceEpoch()) + .arg(QDateTime::currentDateTime().toMSecsSinceEpoch())); + } else if (m_startDate.isValid() && m_endDate.isValid()) { + // both dates are valid + query.append(QString("timestamp BETWEEN '%1' AND '%2' ") + .arg(m_startDate.toMSecsSinceEpoch()) + .arg(m_endDate.toMSecsSinceEpoch())); + } + return query; +} + +QString LogFilter::createSourcesString() const +{ + QString query; + if (!m_sources.isEmpty()) { + if (m_sources.count() == 1) { + query.append(QString("sourceType = '%1' ").arg(m_sources.first())); + } else { + query.append("( "); + foreach (const Logging::LoggingSource &source, m_sources) { + query.append(QString("sourceType = '%1' ").arg(source)); + if (source != m_sources.last()) + query.append("OR "); + } + query.append(") "); + } + } + return query; +} + +QString LogFilter::createLevelsString() const +{ + QString query; + if (!m_levels.isEmpty()) { + if (m_levels.count() == 1) { + query.append(QString("loggingLevel = '%1' ").arg(m_levels.first())); + } else { + query.append("( "); + foreach (const Logging::LoggingLevel &level, m_levels) { + query.append(QString("loggingLevel = '%1' ").arg(level)); + if (level != m_levels.last()) + query.append("OR "); + } + query.append(") "); + } + } + return query; +} + +QString LogFilter::createEventTypesString() const +{ + QString query; + if (!m_eventTypes.isEmpty()) { + if (m_eventTypes.count() == 1) { + query.append(QString("loggingEventType = '%1' ").arg(m_eventTypes.first())); + } else { + query.append("( "); + foreach (const Logging::LoggingEventType &eventType, m_eventTypes) { + query.append(QString("loggingEventType = '%1' ").arg(eventType)); + if (eventType != m_eventTypes.last()) + query.append("OR "); + } + query.append(") "); + } + } + return query; +} + +QString LogFilter::createTypeIdsString() const +{ + QString query; + if (!m_typeIds.isEmpty()) { + if (m_typeIds.count() == 1) { + query.append(QString("typeId = '%1' ").arg(m_typeIds.first().toString())); + } else { + query.append("( "); + foreach (const QUuid &typeId, m_typeIds) { + query.append(QString("typeId = '%1' ").arg(typeId.toString())); + if (typeId != m_typeIds.last()) + query.append("OR "); + } + query.append(") "); + } + } + return query; +} + +QString LogFilter::createDeviceIdString() const +{ + QString query; + if (!m_deviceIds.isEmpty()) { + if (m_deviceIds.count() == 1) { + query.append(QString("deviceId = '%1' ").arg(m_deviceIds.first().toString())); + } else { + query.append("( "); + foreach (const DeviceId &deviceId, m_deviceIds) { + query.append(QString("deviceId = '%1' ").arg(deviceId.toString())); + if (deviceId != m_deviceIds.last()) + query.append("OR "); + } + query.append(") "); + } + } + return query; +} + +QString LogFilter::createValuesString() const +{ + QString query; + if (!m_values.isEmpty()) { + if (m_values.count() == 1) { + query.append(QString("value = '%1' ").arg(m_values.first())); + } else { + query.append("( "); + foreach (const QString &value, m_values) { + query.append(QString("value = '%1' ").arg(value)); + if (value != m_values.last()) + query.append("OR "); + } + query.append(") "); + } + } + return query; +} + +} diff --git a/server/logging/logfilter.h b/server/logging/logfilter.h index e76e0d26..a9b3e99b 100644 --- a/server/logging/logfilter.h +++ b/server/logging/logfilter.h @@ -1,6 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2014 Michael Zanetti * + * Copyright (C) 2015 Simon Stuerz * * * * This file is part of guh. * * * @@ -23,13 +24,65 @@ #include +#include "logging.h" +#include "typeutils.h" + namespace guhserver { class LogFilter { +public: + LogFilter(); + + QString queryString() const; + + void setStartDate(const QDateTime &startDate); + QDateTime startDate() const; + + void setEndDate(const QDateTime &endDate); + QDateTime endDate() const; + + void addLoggingSource(const Logging::LoggingSource &source) ; + QList loggingSources() const; + + void addLoggingLevel(const Logging::LoggingLevel &level); + QList loggingLevels() const; + + void addLoggingEventType(const Logging::LoggingEventType &eventType); + QList loggingEventTypes() const; + + // Valid for LoggingSourceStates, LoggingSourceEvents, LoggingSourceActions, LoggingSourceRules + void addTypeId(const QUuid &typeId); + QList typeIds() const; + + // Valid for LoggingSourceStates, LoggingSourceEvents, LoggingSourceActions + void addDeviceId(const DeviceId &deviceId); + QList deviceIds() const; + + // Valid for LoggingSourceStates + void addValue(const QString &value); + QList values() const; + + bool isEmpty() const; + +private: QDateTime m_startDate; QDateTime m_endDate; + QList m_sources; + QList m_levels; + QList m_eventTypes; + QList m_typeIds; + QList m_deviceIds; + QList m_values; + + QString createDateString() const; + QString createSourcesString() const; + QString createLevelsString() const; + QString createEventTypesString() const; + QString createTypeIdsString() const; + QString createDeviceIdString() const; + QString createValuesString() const; }; } diff --git a/server/logging/logging.h b/server/logging/logging.h index a2401890..500e9b67 100644 --- a/server/logging/logging.h +++ b/server/logging/logging.h @@ -36,7 +36,8 @@ class Logging public: enum LoggingError { LoggingErrorNoError, - LoggingErrorLogEntryNotFound + LoggingErrorLogEntryNotFound, + LoggingErrorInvalidFilterParameter }; enum LoggingSource { diff --git a/server/main.cpp b/server/main.cpp index bd8763d0..658dc56e 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -60,7 +60,7 @@ int main(int argc, char *argv[]) s_loggingFilters.insert("Warnings", true); s_loggingFilters.insert("DeviceManager", true); s_loggingFilters.insert("RuleEngine", true); - s_loggingFilters.insert("Connection", true); + s_loggingFilters.insert("Connection", false); s_loggingFilters.insert("JsonRpc", false); s_loggingFilters.insert("Hardware", false); s_loggingFilters.insert("LogEngine", false);