diff --git a/src/blackmisc/loghandler.cpp b/src/blackmisc/loghandler.cpp new file mode 100644 index 000000000..c54e8e0cf --- /dev/null +++ b/src/blackmisc/loghandler.cpp @@ -0,0 +1,124 @@ +/* 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 "loghandler.h" +#include +#include + +namespace BlackMisc +{ + Q_GLOBAL_STATIC(CLogHandler, g_handler) + + CLogHandler *CLogHandler::instance() + { + Q_ASSERT(! g_handler.isDestroyed()); + return g_handler; + } + + void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message) + { + CStatusMessage statusMessage(type, context, message); + QMetaObject::invokeMethod(CLogHandler::instance(), "logLocalMessage", Q_ARG(BlackMisc::CStatusMessage, statusMessage)); + } + + void CLogHandler::install() + { + m_oldHandler = qInstallMessageHandler(messageHandler); + } + + CLogHandler::CLogHandler() + { + // in case the first call to instance() is in a different thread + moveToThread(QCoreApplication::instance()->thread()); + } + + CLogHandler::~CLogHandler() + { + if (m_oldHandler) + { + qInstallMessageHandler(m_oldHandler); + } + } + + CLogCategoryHandler *CLogHandler::handlerForCategory(const QString &category) + { + if (! m_categoryHandlers.contains(category)) + { + m_categoryHandlers[category] = new CLogCategoryHandler(this, m_enableFallThrough); + } + + return m_categoryHandlers[category]; + } + + QList CLogHandler::handlersForCategory(const QString &category) const + { + QList m_handlers; + for (auto i = m_categoryHandlers.begin(); i != m_categoryHandlers.end(); ++i) + { + if (category.startsWith(i.key())) + { + m_handlers.push_back(i.value()); + } + } + return m_handlers; + } + + void CLogHandler::enableConsoleOutput(bool enable) + { + m_enableFallThrough = enable; + for (auto *handler : m_categoryHandlers.values()) + { + handler->enableConsoleOutput(enable); + } + } + + bool CLogHandler::isFallThroughEnabled(const QList &handlers) const + { + for (auto *handler : handlers) + { + if (handler->m_enableFallThrough != m_enableFallThrough) + { + return handler->m_enableFallThrough; + } + } + return m_enableFallThrough; + } + + void CLogHandler::logLocalMessage(const CStatusMessage &statusMessage) + { + logMessage(statusMessage); + emit localMessageLogged(statusMessage); + } + + void CLogHandler::logRemoteMessage(const CStatusMessage &statusMessage) + { + logMessage(statusMessage); + emit remoteMessageLogged(statusMessage); + } + + void CLogHandler::logMessage(const CStatusMessage &statusMessage) + { + auto handlers = handlersForCategory(statusMessage.getCategory()); + + if (isFallThroughEnabled(handlers)) + { + QtMsgType type; + QString category; + QString message; + statusMessage.toQtLogTriple(&type, &category, &message); + m_oldHandler(type, QMessageLogContext(nullptr, 0, nullptr, qPrintable(category)), message); + } + + for (auto *handler : handlers) + { + emit handler->messageLogged(statusMessage); + } + } + +} diff --git a/src/blackmisc/loghandler.h b/src/blackmisc/loghandler.h new file mode 100644 index 000000000..150d01af1 --- /dev/null +++ b/src/blackmisc/loghandler.h @@ -0,0 +1,101 @@ +/* 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_LOGHANDLER_H +#define BLACKMISC_LOGHANDLER_H + +//! \file + +#include "statusmessage.h" +#include +#include + +namespace BlackMisc +{ + class CLogCategoryHandler; + + /*! + * Class for subscribing to log messages. + */ + class CLogHandler : public QObject + { + Q_OBJECT + + public: + //! \private Constructor. + CLogHandler(); + + //! \private Destructor. + ~CLogHandler(); + + //! Return pointer to the CLogHandler singleton. + //! \warning This can not be called from within a plugin, because the returned instance will be wrong. + static CLogHandler *instance(); + + //! Tell the CLogHandler to install itself with qInstallMessageHandler. + void install(); + + //! Return a category handler for subscribing to all messages whose category string starts with the given prefix. + CLogCategoryHandler *handlerForCategory(const QString &prefix); + + //! Enable or disable the default Qt handler. + void enableConsoleOutput(bool enable); + + signals: + //! Emitted when a message is logged in this process. + void localMessageLogged(const BlackMisc::CStatusMessage &message); + + //! Emitted when a log message is relayed from a different process. + void remoteMessageLogged(const BlackMisc::CStatusMessage &message); + + public slots: + //! Called by our QtMessageHandler to log a message. + void logLocalMessage(const BlackMisc::CStatusMessage &message); + + //! Called by the context to relay a message. + void logRemoteMessage(const BlackMisc::CStatusMessage &message); + + private: + void logMessage(const BlackMisc::CStatusMessage &message); + QtMessageHandler m_oldHandler = nullptr; + bool m_enableFallThrough = true; + bool isFallThroughEnabled(const QList &handlers) const; + QMap m_categoryHandlers; + QList handlersForCategory(const QString &category) const; + }; + + /*! + * A class for subscribing to log messages in particular categories. + * \sa CLogHandler::handlerForCategory + */ + class CLogCategoryHandler : public QObject + { + Q_OBJECT + + public: + /*! + * Enable or disable the default Qt handler for messages in relevant categories. + * This can override the setting of the parent CLogHandler. + */ + void enableConsoleOutput(bool enable) { m_enableFallThrough = enable; } + + signals: + /*! + * Emitted when a message is logged in a relevant category. + */ + void messageLogged(const CStatusMessage &message); + + private: + friend class CLogHandler; + CLogCategoryHandler(QObject *parent, bool enableFallThrough) : QObject(parent), m_enableFallThrough(enableFallThrough) {} + bool m_enableFallThrough; + }; +} + +#endif