From 067344df24c816b0d1534c7a67984bc6dcf3e43b Mon Sep 17 00:00:00 2001 From: Mathew Sutcliffe Date: Tue, 14 Oct 2014 00:02:16 +0100 Subject: [PATCH] refs #336 Added class CLogCategory to represent a log category. The category logic of CLogMessage is moved into this new class. --- src/blackmisc/blackmiscfreefunctions.cpp | 2 + src/blackmisc/logcategory.cpp | 67 ++++++++++ src/blackmisc/logcategory.h | 153 +++++++++++++++++++++++ src/blackmisc/logcategorylist.cpp | 58 +++++++++ src/blackmisc/logcategorylist.h | 144 +++++++++++++++++++++ src/blackmisc/loghandler.cpp | 6 +- src/blackmisc/loghandler.h | 2 +- src/blackmisc/logmessage.cpp | 25 ++-- src/blackmisc/logmessage.h | 80 +++++------- src/blackmisc/statusmessage.cpp | 21 ++-- src/blackmisc/statusmessage.h | 9 +- src/blackmisc/statusmessagelist.cpp | 4 +- src/blackmisc/statusmessagelist.h | 2 +- 13 files changed, 485 insertions(+), 88 deletions(-) create mode 100644 src/blackmisc/logcategory.cpp create mode 100644 src/blackmisc/logcategory.h create mode 100644 src/blackmisc/logcategorylist.cpp create mode 100644 src/blackmisc/logcategorylist.h diff --git a/src/blackmisc/blackmiscfreefunctions.cpp b/src/blackmisc/blackmiscfreefunctions.cpp index 3f06a6f53..6b603918c 100644 --- a/src/blackmisc/blackmiscfreefunctions.cpp +++ b/src/blackmisc/blackmiscfreefunctions.cpp @@ -173,6 +173,8 @@ void BlackMisc::registerMetadata() CIcon::registerMetadata(); CIconList::registerMetadata(); CHotkeyFunction::registerMetadata(); + CLogCategory::registerMetadata(); + CLogCategoryList::registerMetadata(); // sub namespaces PhysicalQuantities::registerMetadata(); diff --git a/src/blackmisc/logcategory.cpp b/src/blackmisc/logcategory.cpp new file mode 100644 index 000000000..b05465532 --- /dev/null +++ b/src/blackmisc/logcategory.cpp @@ -0,0 +1,67 @@ +/* Copyright (C) 2014 + * Swift Project Community / Contributors + * + * This file is part of Swift Project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "logcategory.h" + +namespace BlackMisc +{ + void CLogCategory::registerMetadata() + { + qRegisterMetaType(); + qDBusRegisterMetaType(); + } + + uint CLogCategory::getValueHash() const + { + return qHash(TupleConverter::toMetaTuple(*this)); + } + + bool CLogCategory::operator ==(const CLogCategory &other) const + { + return TupleConverter::toMetaTuple(*this) == TupleConverter::toMetaTuple(other); + } + + bool CLogCategory::operator !=(const CLogCategory &other) const + { + return TupleConverter::toMetaTuple(*this) != TupleConverter::toMetaTuple(other); + } + + QString CLogCategory::convertToQString(bool i18n) const + { + Q_UNUSED(i18n); + return m_string; + } + + int CLogCategory::getMetaTypeId() const + { + return qMetaTypeId(); + } + + bool CLogCategory::isA(int metaTypeId) const + { + if (metaTypeId == qMetaTypeId()) { return true; } + return this->CValueObject::isA(metaTypeId); + } + + int CLogCategory::compareImpl(const CValueObject &otherBase) const + { + const auto &other = static_cast(otherBase); + return compare(TupleConverter::toMetaTuple(*this), TupleConverter::toMetaTuple(other)); + } + + void CLogCategory::marshallToDbus(QDBusArgument &argument) const + { + argument << TupleConverter::toMetaTuple(*this); + } + + void CLogCategory::unmarshallFromDbus(const QDBusArgument &argument) + { + argument >> TupleConverter::toMetaTuple(*this); + } +} diff --git a/src/blackmisc/logcategory.h b/src/blackmisc/logcategory.h new file mode 100644 index 000000000..57d3e828a --- /dev/null +++ b/src/blackmisc/logcategory.h @@ -0,0 +1,153 @@ +/* Copyright (C) 2014 + * Swift Project Community / Contributors + * + * This file is part of Swift Project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#ifndef BLACKMISC_LOGCATEGORY_H +#define BLACKMISC_LOGCATEGORY_H + +//! \file + +#include "sequence.h" + +namespace BlackMisc +{ + /*! + * A log category is an arbitrary string tag which can be attached to log messages. + * + * A log handler can filter messages based on their categories. + */ + class CLogCategory : public CValueObject + { + public: + //! \name Predefined special categories (public static methods) + //! @{ + + //! Uncategorized + static const CLogCategory &uncategorized() + { + static const CLogCategory cat { "swift.uncategorized" }; + return cat; + } + + //! Validation + static const CLogCategory &validation() + { + static const CLogCategory cat { "swift.validation" }; + return cat; + } + + //! Settings updates + static const CLogCategory &settingsUpdate() + { + static const CLogCategory cat { "swift.settings.update" }; + return cat; + } + + //! Contexts + static const CLogCategory &context() + { + static const CLogCategory cat { "swift.context" }; + return cat; + } + + //! Context slots + static const CLogCategory &contextSlot() + { + static const CLogCategory cat { "swift.context.slot" }; + return cat; + } + + //! GUI components + static const CLogCategory &guiComponent() + { + static const CLogCategory cat { "swift.gui.component" }; + return cat; + } + + //! All predefined special categories + static const QList &allSpecialCategories() + { + static const QList cats + { + uncategorized(), + validation(), + settingsUpdate(), + context(), + contextSlot(), + guiComponent() + }; + return cats; + } + + //! @} + + //! Constructor. + CLogCategory() = default; + + //! Constructor. + CLogCategory(const QString &categoryString) : m_string(categoryString) {} + + //! Constructor. + CLogCategory(const char *categoryString) : m_string(categoryString) {} + + //! Returns true if the category string starts with the given prefix. + bool startsWith(const QString &prefix) const { return m_string.startsWith(prefix); } + + //! Returns true if the category string ends with the given suffix. + bool endsWith(const QString &suffix) const { return m_string.endsWith(suffix); } + + //! Returns true if the category string contains the given substring. + bool contains(const QString &substring) const { return m_string.contains(substring); } + + //! Register metadata + static void registerMetadata(); + + //! \copydoc CValueObject::toQVariant + virtual QVariant toQVariant() const override { return QVariant::fromValue(*this); } + + //! \copydoc CValueObject::convertFromQVariant + virtual void convertFromQVariant(const QVariant &variant) override { BlackMisc::setFromQVariant(this, variant); } + + //! \copydoc CValueObject::getValueHash + virtual uint getValueHash() const override; + + //! Equal operator + bool operator ==(const CLogCategory &other) const; + + //! Not equal operator + bool operator !=(const CLogCategory &other) const; + + protected: + //! \copydoc CValueObject::convertToQString() + virtual QString convertToQString(bool i18n = false) const override; + + //! \copydoc CValueObject::getMetaTypeId + virtual int getMetaTypeId() const override; + + //! \copydoc CValueObject::isA + virtual bool isA(int metaTypeId) const override; + + //! \copydoc CValueObject::compareImpl + virtual int compareImpl(const CValueObject &other) const override; + + //! \copydoc CValueObject::marshallToDbus() + virtual void marshallToDbus(QDBusArgument &argument) const override; + + //! \copydoc CValueObject::marshallFromDbus() + virtual void unmarshallFromDbus(const QDBusArgument &argument) override; + + private: + BLACK_ENABLE_TUPLE_CONVERSION(CLogCategory) + QString m_string; + }; +} + +Q_DECLARE_METATYPE(BlackMisc::CLogCategory) +BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::CLogCategory, (o.m_string)) + +#endif \ No newline at end of file diff --git a/src/blackmisc/logcategorylist.cpp b/src/blackmisc/logcategorylist.cpp new file mode 100644 index 000000000..a1c7b002f --- /dev/null +++ b/src/blackmisc/logcategorylist.cpp @@ -0,0 +1,58 @@ +/* Copyright (C) 2014 + * Swift Project Community / Contributors + * + * This file is part of Swift Project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "logcategorylist.h" + +namespace BlackMisc +{ + void CLogCategoryList::appendCategoriesFromMetaObject(const QMetaObject &metaObject) + { + for (auto *meta = &metaObject; meta; meta = meta->superClass()) + { + push_back(meta->className()); + } + } + + QStringList CLogCategoryList::toQStringList() const + { + return transform([](const CLogCategory &cat) { return cat.toQString(); }); + } + + QString CLogCategoryList::convertToQString(bool i18n) const + { + Q_UNUSED(i18n); // log categories are always Latin-1 + return toQStringList().join("|"); + } + + CLogCategoryList CLogCategoryList::fromQStringList(const QStringList &stringList) + { + return makeRange(Iterators::makeTransformIterator(stringList.begin(), [](const QString &str) { return CLogCategory{str}; }), stringList.end()); + } + + CLogCategoryList CLogCategoryList::fromQString(const QString &string) + { + return fromQStringList(string.split("|")); + } + + bool CLogCategoryList::anyStartWith(const QString &prefix) const + { + return containsBy([ = ](const CLogCategory &cat) { return cat.startsWith(prefix); }); + } + + bool CLogCategoryList::anyEndWith(const QString &suffix) const + { + return containsBy([ = ](const CLogCategory &cat) { return cat.endsWith(suffix); }); + } + + void CLogCategoryList::registerMetadata() + { + qRegisterMetaType(); + qDBusRegisterMetaType(); + } +} diff --git a/src/blackmisc/logcategorylist.h b/src/blackmisc/logcategorylist.h new file mode 100644 index 000000000..be438d6c5 --- /dev/null +++ b/src/blackmisc/logcategorylist.h @@ -0,0 +1,144 @@ +/* Copyright (C) 2014 + * Swift Project Community / Contributors + * + * This file is part of Swift Project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#ifndef BLACKMISC_LOGCATEGORYLIST_H +#define BLACKMISC_LOGCATEGORYLIST_H + +//! \file + +#include "logcategory.h" +#include "sequence.h" +#include "collection.h" +#include +#include +#include + +namespace BlackMisc +{ + namespace Private + { + //! \private Trait to detect whether a class T has a member function called getLogCategories + template class HasGetLogCategories + { + struct Base { int getLogCategories; }; + struct Derived : T, Base {}; + template struct TypeCheck {}; + template static std::false_type test(TypeCheck *); + template static std::true_type test(...); + public: + using type = decltype(test(nullptr)); + }; + } + + /*! + * A sequence of log categories. + */ + class CLogCategoryList : public CSequence + { + public: + //! Empty constructor. + CLogCategoryList() = default; + + //! Copy constructor. + CLogCategoryList(const CLogCategoryList &) = default; + + //! Copy construct from superclass instance. + CLogCategoryList(const CSequence &other) : CSequence(other) {} + + //! Initializer list constructor. + CLogCategoryList(std::initializer_list il) : CSequence(il) {} + + //! Copy assignment. + CLogCategoryList &operator =(const CLogCategoryList &) = default; + + //! Copy assign from superclass instance. + CLogCategoryList &operator =(const CSequence &other) { CSequence::operator =(other); return *this; } + + /*! + * Construct by extracting categories from a class T. + * + * If T has a member function getLogCategories, then this will be called. + * If T inherits from QObject then we get the name of the class and all its superclasses. + * If T is registered with QMetaType then we get the name of the class. + * If more than one of the above cases is true, then all are combined into the list. + * If none of the above cases is true, then the constructed list will be the uncategorized category. + * + * This constructor will be disabled if T is not a class type. + * + * \param pointer The value of pointer is unimportant. Only the static type T is considered. + * It is legal to pass static_cast(nullptr), but in member functions passing the this pointer is easier. + */ + template ::value>::type> + CLogCategoryList(const T *pointer) : CLogCategoryList(fromClass()) { Q_UNUSED(pointer); } + + //! Convert each of the categories to a QString and return the result as a QStringList. + QStringList toQStringList() const; + + //! Convert a string list, such as that returned by toQStringList(), into a CLogCategoryList. + static CLogCategoryList fromQStringList(const QStringList &stringList); + + //! Convert a string, such as that returned by toQString(), into a CLogCategoryList. + static CLogCategoryList fromQString(const QString &string); + + //! Returns true if any of the categories in the list start with the given prefix. + bool anyStartWith(const QString &prefix) const; + + //! Returns true if any of the categories in the list end with the given suffix. + bool anyEndWith(const QString &suffix) const; + + //! Register metadata + static void registerMetadata(); + + protected: + //! \copydoc BlackMisc::CValueObject::convertToQString + virtual QString convertToQString(bool i18n = false) const override; + + private: + /* + * Templates used by the constructor template: + */ + template + struct tag {}; + + template + static const CLogCategoryList &fromClass() + { + static_assert(sizeof(T) > 0, "T must be a complete type, not forward declared"); + static QThreadStorage list; + if (! list.hasLocalData()) + { + list.localData().appendCategoriesFromMemberFunction(tag(), typename Private::HasGetLogCategories::type()); + list.localData().appendCategoriesFromMetaType(tag(), std::integral_constant::Defined>()); + list.localData().appendCategoriesFromMetaObject(tag(), std::is_base_of()); + if (list.localData().isEmpty()) { list.localData().push_back(CLogCategory::uncategorized()); } + } + return list.localData(); + } + + template + void appendCategoriesFromMemberFunction(tag, std::true_type) { push_back(T::getLogCategories()); } + void appendCategoriesFromMemberFunction(...) {} + + template + void appendCategoriesFromMetaType(tag, std::true_type) { push_back(QMetaType::typeName(qMetaTypeId())); } + void appendCategoriesFromMetaType(...) {} + + template + void appendCategoriesFromMetaObject(tag, std::true_type) { appendCategoriesFromMetaObject(T::staticMetaObject); } + void appendCategoriesFromMetaObject(...) {} + + void appendCategoriesFromMetaObject(const QMetaObject &); + }; +} + +Q_DECLARE_METATYPE(BlackMisc::CLogCategoryList) +Q_DECLARE_METATYPE(BlackMisc::CCollection) +Q_DECLARE_METATYPE(BlackMisc::CSequence) + +#endif \ No newline at end of file diff --git a/src/blackmisc/loghandler.cpp b/src/blackmisc/loghandler.cpp index 7b7887200..56e4e7612 100644 --- a/src/blackmisc/loghandler.cpp +++ b/src/blackmisc/loghandler.cpp @@ -64,12 +64,12 @@ namespace BlackMisc return m_categoryPrefixHandlers[category]; } - QList CLogHandler::handlersForCategory(const QString &category) const + QList CLogHandler::handlersForCategories(const CLogCategoryList &categories) const { QList m_handlers; for (auto i = m_categoryPrefixHandlers.begin(); i != m_categoryPrefixHandlers.end(); ++i) { - if (category.startsWith(i.key())) + if (categories.anyStartWith(i.key())) { m_handlers.push_back(i.value()); } @@ -114,7 +114,7 @@ namespace BlackMisc void CLogHandler::logMessage(const CStatusMessage &statusMessage) { - auto handlers = handlersForCategory(statusMessage.getCategory()); + auto handlers = handlersForCategories(statusMessage.getCategories()); if (isFallThroughEnabled(handlers)) { diff --git a/src/blackmisc/loghandler.h b/src/blackmisc/loghandler.h index ba3bb44fd..f0645ddc4 100644 --- a/src/blackmisc/loghandler.h +++ b/src/blackmisc/loghandler.h @@ -69,7 +69,7 @@ namespace BlackMisc bool m_enableFallThrough = true; bool isFallThroughEnabled(const QList &handlers) const; QMap m_categoryPrefixHandlers; - QList handlersForCategory(const QString &category) const; + QList handlersForCategories(const CLogCategoryList &categories) const; }; /*! diff --git a/src/blackmisc/logmessage.cpp b/src/blackmisc/logmessage.cpp index ec1fd83c4..24b0e2a3c 100644 --- a/src/blackmisc/logmessage.cpp +++ b/src/blackmisc/logmessage.cpp @@ -13,34 +13,29 @@ namespace BlackMisc { - CLogMessage &CLogMessage::debugImpl(QString format, QString category) + CLogMessage &CLogMessage::debug() { m_severity = CStatusMessage::SeverityDebug; - m_category = category; - m_message = format; return *this; } - CLogMessage &CLogMessage::infoImpl(QString format, QString category) + CLogMessage &CLogMessage::info(QString format) { m_severity = CStatusMessage::SeverityInfo; - m_category = category; m_message = format; return *this; } - CLogMessage &CLogMessage::warningImpl(QString format, QString category) + CLogMessage &CLogMessage::warning(QString format) { m_severity = CStatusMessage::SeverityWarning; - m_category = category; m_message = format; return *this; } - CLogMessage &CLogMessage::errorImpl(QString format, QString category) + CLogMessage &CLogMessage::error(QString format) { m_severity = CStatusMessage::SeverityError; - m_category = category; m_message = format; return *this; } @@ -48,7 +43,7 @@ namespace BlackMisc CLogMessage::operator CStatusMessage() { m_redundant = true; - return { m_category, m_severity, message() }; + return { m_categories, m_severity, message() }; } CLogMessage::operator CVariant() @@ -62,21 +57,21 @@ namespace BlackMisc // FIXME hack to avoid putting quote characters around the message // should be safe, but still it's horrible, we could directly call qt_message_output instead - QByteArray category = encodedCategory(); + QByteArray category = qtCategory(); QDebug debug = ostream(category); auto &stream = **reinterpret_cast(&debug); // should be safe because it is relying on Qt's guarantee of ABI compatibility stream << message(); } - QByteArray CLogMessage::encodedCategory() const + QByteArray CLogMessage::qtCategory() const { - if (m_category.isEmpty()) + if (m_categories.isEmpty()) { return {}; } else { - QString category = m_category; + QString category = m_categories.toQString(); if (m_severity == CStatusMessage::SeverityDebug) { category = CLogMessageHelper::addDebugFlag(category); } if (m_redundant) { category = CLogMessageHelper::addRedundantFlag(category); } return category.toLatin1(); @@ -85,7 +80,7 @@ namespace BlackMisc QDebug CLogMessage::ostream(const QByteArray &category) const { - if (m_category.isEmpty()) + if (m_categories.isEmpty()) { switch (m_severity) { diff --git a/src/blackmisc/logmessage.h b/src/blackmisc/logmessage.h index 3ca2d4904..cec05113e 100644 --- a/src/blackmisc/logmessage.h +++ b/src/blackmisc/logmessage.h @@ -13,6 +13,7 @@ //! \file #include "statusmessage.h" +#include "logcategorylist.h" #include "index_sequence.h" #include #include @@ -64,16 +65,29 @@ namespace BlackMisc * The member functions debug, info, warning, error, and the stream operators all return a reference to *this, * so they can be chained together. * - * The category string identifies the origin of the message (e.g. network system) or its subtype (e.g. validation). + * The categories are arbitrary string tags which can be attached to the message to categorize it. + * A message can have more than one category. The categories can be used for filtering by message handlers. */ class CLogMessage { public: - //! Constructor. + //! Construct a message with the "uncategorized" category. CLogMessage() {} - //! Constructor taking filename, line number, and function name, for verbose debug messages. - CLogMessage(const char *file, int line, const char *function): m_logger(file, line, function) {} + //! Constructor taking filename, line number, and function name, for uncategorized verbose debug messages. + CLogMessage(const char *file, int line, const char *function) : m_logger(file, line, function) {} + + //! Construct a message with some specific category. + CLogMessage(const CLogCategory &category) : m_categories({ category }) {} + + //! Construct a message with some specific categories. + CLogMessage(const CLogCategoryList &categories) : m_categories(categories) {} + + //! Construct a message with some specific categories. + CLogMessage(const CLogCategoryList &categories, const CLogCategory &extra) : CLogMessage(categories) { m_categories.push_back(extra); } + + //! Construct a message with some specific categories. + CLogMessage(const CLogCategoryList &categories, const CLogCategoryList &extra) : CLogMessage(categories) { m_categories.push_back(extra); } //! Destructor. This actually emits the message. ~CLogMessage(); @@ -84,48 +98,17 @@ namespace BlackMisc //! Convert to CVariant for returning the message directly from the function which generated it. operator CVariant(); - //! Set the severity to debug, with the default category. - CLogMessage &debug() { return debugImpl(""); } + //! Set the severity to debug. + CLogMessage &debug(); - //! Set the severity to debug, with a category string. - CLogMessage &debug(QString category) { return debugImpl("", category); } + //! Set the severity to info, providing a format string. + CLogMessage &info(QString format); - //! Set the severity to debug, with the category string obtained from the getMessageCategory method of the sender. - //! \note To avoid overload ambiguity, this method is disabled if T is not a class type. - template ::type>::value>::type> - CLogMessage &debug(T *sender) { Q_UNUSED(sender); return debugImpl("", sender->getMessageCategory()); } + //! Set the severity to warning, providing a format string. + CLogMessage &warning(QString format); - //! Set the severity to info, providing a format string, with the default category. - CLogMessage &info(QString format) { return infoImpl(format); } - - //! Set the severity to info, providing a format string and category string. - CLogMessage &info(QString category, QString format) { return infoImpl(format, category); } - - //! Set the severity to info, providing a format string, with the category string obtained from the getMessageCategory method of the sender. - //! \note To avoid overload ambiguity, this method is disabled if T is not a class type. - template ::type>::value>::type> - CLogMessage &info(T *sender, QString format) { Q_UNUSED(sender); return infoImpl(format, sender->getMessageCategory()); } - - //! Set the severity to warning, providing a format string, with the default category. - CLogMessage &warning(QString format) { return warningImpl(format); } - - //! Set the severity to warning, providing a format string and category string. - CLogMessage &warning(QString category, QString format) { return warningImpl(format, category); } - - //! Set the severity to warning, providing a format string, with the category string obtained from the getMessageCategory method of the sender. - //! \note To avoid overload ambiguity, this method is disabled if T is not a class type. - template ::type>::value>::type> - CLogMessage &warning(T *sender, QString format) { Q_UNUSED(sender); return warningImpl(format, sender->getMessageCategory()); } - - //! Set the severity to error, providing a format string, with the default category. - CLogMessage &error(QString format) { return errorImpl(format); } - - //! Set the severity to error, providing a format string and category string. - CLogMessage &error(QString category, QString format) { return errorImpl(format, category); } - - //! Set the severity to error, providing a format string, with the category string obtained from the getMessageCategory method of the sender. - template ::type>::value>::type> - CLogMessage &error(T *sender, QString format) { Q_UNUSED(sender); return errorImpl(format, sender->getMessageCategory()); } + //! Set the severity to error, providing a format string. + CLogMessage &error(QString format); //! Streaming operators. //! \details If the format string is empty, the message will consist of all streamed values separated by spaces. @@ -147,24 +130,17 @@ namespace BlackMisc CLogMessage &operator <<(const CValueObject &v) { return arg(v.toQString()); } //! @} - //! The default message category which is used if a category is not provided. - static const char *defaultMessageCategory() { return "swift"; } - private: QMessageLogger m_logger; CStatusMessage::StatusSeverity m_severity = CStatusMessage::SeverityDebug; - QString m_category; + CLogCategoryList m_categories = CLogCategoryList { CLogCategory::uncategorized() }; QString m_message; QStringList m_args; bool m_redundant = false; - CLogMessage &debugImpl(QString format, QString category = defaultMessageCategory()); - CLogMessage &infoImpl(QString format, QString category = defaultMessageCategory()); - CLogMessage &warningImpl(QString format, QString category = defaultMessageCategory()); - CLogMessage &errorImpl(QString format, QString category = defaultMessageCategory()); CLogMessage &arg(QString value) { m_args.push_back(value); return *this; } QString message() const; - QByteArray encodedCategory() const; + QByteArray qtCategory() const; QDebug ostream(const QByteArray &category) const; }; } diff --git a/src/blackmisc/statusmessage.cpp b/src/blackmisc/statusmessage.cpp index 2f1eeaf5a..2be8723bc 100644 --- a/src/blackmisc/statusmessage.cpp +++ b/src/blackmisc/statusmessage.cpp @@ -33,16 +33,17 @@ namespace BlackMisc : m_severity(severity), m_message(message), m_timestamp(QDateTime::currentDateTimeUtc()) {} - CStatusMessage::CStatusMessage(const QString &category, StatusSeverity severity, const QString &message) - : m_category(category), m_severity(severity), m_message(message), m_timestamp(QDateTime::currentDateTimeUtc()) + CStatusMessage::CStatusMessage(const CLogCategoryList &categories, StatusSeverity severity, const QString &message) + : m_categories(categories), m_severity(severity), m_message(message), m_timestamp(QDateTime::currentDateTimeUtc()) {} CStatusMessage::CStatusMessage(QtMsgType type, const QMessageLogContext &context, const QString &message) - : CStatusMessage(context.category, SeverityInfo, message) + : CStatusMessage(message) { - m_redundant = CLogMessageHelper::hasRedundantFlag(m_category); - bool debug = CLogMessageHelper::hasDebugFlag(m_category); - m_category = CLogMessageHelper::stripFlags(m_category); + m_redundant = CLogMessageHelper::hasRedundantFlag(context.category); + bool debug = CLogMessageHelper::hasDebugFlag(context.category); + auto categories = CLogMessageHelper::stripFlags(context.category); + m_categories = CLogCategoryList::fromQString(categories); switch(type) { @@ -85,7 +86,7 @@ namespace BlackMisc */ void CStatusMessage::toQtLogTriple(QtMsgType *o_type, QString *o_category, QString *o_message) const { - QString category = m_category; + auto category = m_categories.toQString(); if (this->m_severity == SeverityDebug && ! category.isEmpty()) { category = CLogMessageHelper::addDebugFlag(category); @@ -133,7 +134,7 @@ namespace BlackMisc { QString s("Category: "); - s.append(this->m_category); + s.append(this->m_categories.toQString()); s.append(" Severity: "); s.append(QString::number(this->m_severity)); @@ -212,7 +213,7 @@ namespace BlackMisc return this->m_timestamp.toString("HH:mm::ss.zzz"); } case IndexCategory: - return QVariant(this->m_category); + return QVariant(this->m_categories.toQString()); default: break; } @@ -245,7 +246,7 @@ namespace BlackMisc this->m_severity = static_cast(variant.value()); break; case IndexCategory: - this->m_category = variant.value(); + this->m_categories = variant.value(); break; default: CValueObject::setPropertyByIndex(variant, index); diff --git a/src/blackmisc/statusmessage.h b/src/blackmisc/statusmessage.h index 297c684c0..e4d397d45 100644 --- a/src/blackmisc/statusmessage.h +++ b/src/blackmisc/statusmessage.h @@ -14,6 +14,7 @@ #include "icon.h" #include "propertyindex.h" +#include "logcategorylist.h" #include namespace BlackMisc @@ -55,7 +56,7 @@ namespace BlackMisc CStatusMessage(StatusSeverity severity, const QString &message); //! Constructor - CStatusMessage(const QString &category, StatusSeverity severity, const QString &message); + CStatusMessage(const CLogCategoryList &categories, StatusSeverity severity, const QString &message); //! Construct from a Qt logging triple //! \sa QtMessageHandler @@ -72,7 +73,7 @@ namespace BlackMisc bool operator !=(const CStatusMessage &other) const; //! Message category - QString getCategory() const { return this->m_category; } + const CLogCategoryList &getCategories() const { return this->m_categories; } //! Message severity StatusSeverity getSeverity() const { return this->m_severity; } @@ -119,7 +120,7 @@ namespace BlackMisc private: BLACK_ENABLE_TUPLE_CONVERSION(CStatusMessage) - QString m_category; + CLogCategoryList m_categories; StatusSeverity m_severity; QString m_message; QDateTime m_timestamp; @@ -130,7 +131,7 @@ namespace BlackMisc BLACK_DECLARE_TUPLE_CONVERSION(BlackMisc::CStatusMessage, ( - o.m_category, + o.m_categories, o.m_severity, o.m_message, o.m_timestamp, diff --git a/src/blackmisc/statusmessagelist.cpp b/src/blackmisc/statusmessagelist.cpp index 89af82aed..aea7fb22a 100644 --- a/src/blackmisc/statusmessagelist.cpp +++ b/src/blackmisc/statusmessagelist.cpp @@ -22,9 +22,9 @@ namespace BlackMisc /* * Messages by type */ - CStatusMessageList CStatusMessageList::findByCategory(const QString &category) const + CStatusMessageList CStatusMessageList::findByCategory(const CLogCategory &category) const { - return this->findBy(&CStatusMessage::getCategory, category); + return this->findBy([ & ](const CStatusMessage &msg) { return msg.getCategories().contains(category); }); } /* diff --git a/src/blackmisc/statusmessagelist.h b/src/blackmisc/statusmessagelist.h index 4d8b74c7d..e3933a2e6 100644 --- a/src/blackmisc/statusmessagelist.h +++ b/src/blackmisc/statusmessagelist.h @@ -33,7 +33,7 @@ namespace BlackMisc CStatusMessageList(const CSequence &other); //! Find by type - CStatusMessageList findByCategory(const QString &category) const; + CStatusMessageList findByCategory(const CLogCategory &category) const; //! Find by severity CStatusMessageList findBySeverity(CStatusMessage::StatusSeverity severity) const;