From be4b4492ad11f710a6bbc0223968ebf9aecf8a85 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Tue, 8 Apr 2014 18:13:56 +0200 Subject: [PATCH] refs #199, redirection of output streams (qDebug, qWarning, qCritical) This is done in a common base class (of impl/proxy) --- src/blackcore/context_application_base.cpp | 145 ++++++++++++++++++++ src/blackcore/context_application_base.h | 90 ++++++++++++ src/blackcore/context_application_impl.cpp | 7 +- src/blackcore/context_application_impl.h | 2 +- src/blackcore/context_application_proxy.cpp | 2 +- src/blackcore/context_application_proxy.h | 4 +- 6 files changed, 242 insertions(+), 8 deletions(-) create mode 100644 src/blackcore/context_application_base.cpp create mode 100644 src/blackcore/context_application_base.h diff --git a/src/blackcore/context_application_base.cpp b/src/blackcore/context_application_base.cpp new file mode 100644 index 000000000..71661ef0e --- /dev/null +++ b/src/blackcore/context_application_base.cpp @@ -0,0 +1,145 @@ +#include "blackcore/context_application_base.h" +#include "blackmisc/statusmessage.h" +#include +#include + + +using namespace BlackMisc; + +namespace BlackCore +{ + + QList CContextApplicationBase::s_contexts = QList(); + QtMessageHandler CContextApplicationBase::s_oldHandler = nullptr; + + /* + * Constructor + */ + CContextApplicationBase::CContextApplicationBase(CRuntimeConfig::ContextMode mode, CRuntime *runtime) : + IContextApplication(mode, runtime), m_outputRedirectionLevel(IContextApplication::RedirectNone), m_redirectedOutputRedirectionLevel(IContextApplication::RedirectNone) + { + if (CContextApplicationBase::s_contexts.isEmpty()) + CContextApplicationBase::s_oldHandler = qInstallMessageHandler(CContextApplicationBase::messageHandlerDispatch); + CContextApplicationBase::s_contexts.append(this); + } + + /* + * Output data from redirect signal + */ + void CContextApplicationBase::setStreamingForRedirectedOutputLevel(RedirectionLevel redirectionLevel) + { + disconnect(this, &IContextApplication::redirectedOutput, this, &CContextApplicationBase::streamRedirectedOutput); + if (redirectionLevel != RedirectNone) + connect(this, &IContextApplication::redirectedOutput, this, &CContextApplicationBase::streamRedirectedOutput); + this->m_redirectedOutputRedirectionLevel = redirectionLevel; + } + + /* + * Process event in object's thread, used to emit signal from other thread + */ + bool CContextApplicationBase::event(QEvent *event) + { + if (event->type() == CApplicationEvent::eventType()) + { + CApplicationEvent *e = static_cast(event); + emit this->redirectedOutput(e->m_message, this->getUniqueId()); + return true; + } + return IContextApplication::event(event); + } + + /* + * Reset output redirection + */ + void CContextApplicationBase::resetOutputRedirection() + { + qInstallMessageHandler(0); + } + + /* + * Dispatch message + */ + void CContextApplicationBase::messageHandlerDispatch(QtMsgType type, const QMessageLogContext &messageContext, const QString &message) + { + if (CContextApplicationBase::s_oldHandler) CContextApplicationBase::s_oldHandler(type, messageContext, message); + if (CContextApplicationBase::s_contexts.isEmpty()) return; + CContextApplicationBase *ctx; + foreach(ctx, CContextApplicationBase::s_contexts) + { + ctx->messageHandler(type, messageContext, message); + } + } + + /* + * Handle message + */ + void CContextApplicationBase::messageHandler(QtMsgType type, const QMessageLogContext &messageContext, const QString &message) + { + Q_UNUSED(messageContext); + if (this->m_outputRedirectionLevel == RedirectNone) return; + CStatusMessage m(CStatusMessage::TypeStdoutRedirect, CStatusMessage::SeverityInfo, message); + switch (type) + { + case QtDebugMsg: + if (this->m_outputRedirectionLevel != RedirectAllOutput) return; + break; + case QtWarningMsg: + if (this->m_outputRedirectionLevel == RedirectAllOutput) return; + if (this->m_outputRedirectionLevel == RedirectError) return; + m.setSeverity(CStatusMessage::SeverityWarning); + break; + case QtCriticalMsg: + if (this->m_outputRedirectionLevel != RedirectError) return; + m.setSeverity(CStatusMessage::SeverityError); + break; + case QtFatalMsg: + if (this->m_outputRedirectionLevel != RedirectError) return; + m.setSeverity(CStatusMessage::SeverityError); + break; + default: + break; + } + + // emit signal, based on thread id + if (QThread::currentThread() == this->thread()) + { + // same thread, can emit directly + emit this->redirectedOutput(m, this->getUniqueId()); + } + else + { + // different threads, use event. + // in this event the same redirect as above will emiited, only that this is then + // done in the same thread as the parent object + CApplicationEvent *e = new CApplicationEvent(m, this->getUniqueId()); + QCoreApplication::postEvent(this, e); + } + } + + /* + * Redirected output + */ + void CContextApplicationBase::streamRedirectedOutput(const CStatusMessage &message, qint64 contextId) + { + if (this->getUniqueId() == contextId) return; // avoid infinite output + if (this->m_redirectedOutputRedirectionLevel == RedirectNone) return; + + if (message.isEmpty()) return; + switch (message.getSeverity()) + { + case CStatusMessage::SeverityInfo: + if (this->m_redirectedOutputRedirectionLevel != RedirectAllOutput) return; + qDebug() << message.getMessage(); + break; + case CStatusMessage::SeverityWarning: + if (this->m_redirectedOutputRedirectionLevel == RedirectAllOutput) return; + if (this->m_redirectedOutputRedirectionLevel == RedirectError) return; + qWarning() << message.getMessage(); + break; + case CStatusMessage::SeverityError: + if (this->m_redirectedOutputRedirectionLevel != RedirectError) return; + qCritical() << message.getMessage(); + break; + } + } +} // namespace diff --git a/src/blackcore/context_application_base.h b/src/blackcore/context_application_base.h new file mode 100644 index 000000000..f1f8e4983 --- /dev/null +++ b/src/blackcore/context_application_base.h @@ -0,0 +1,90 @@ +#ifndef BLACKCORE_CONTEXT_APPLICATION_BASE_H +#define BLACKCORE_CONTEXT_APPLICATION_BASE_H + +#include "blackcore/context_application.h" +#include + +namespace BlackCore +{ + /*! + * \brief Class, implementing streaming handling for application context + */ + class CContextApplicationBase : public IContextApplication + { + + public: + + //! Destructor + virtual ~CContextApplicationBase() {} + + //! \copydoc IContextApplication::getOutputRedirectionLevel + virtual RedirectionLevel getOutputRedirectionLevel() const override { return this->m_outputRedirectionLevel; } + + //! \copydoc IContextApplication::setOutputRedirectionLevel + virtual void setOutputRedirectionLevel(RedirectionLevel redirectionLevel) override { this->m_outputRedirectionLevel = redirectionLevel; } + + //! \copydoc IContextApplication::getStreamingForRedirectedOutputLevel + virtual RedirectionLevel getStreamingForRedirectedOutputLevel() const override { return this->m_redirectedOutputRedirectionLevel; } + + //! \copydoc IContextApplication::setStreamingForRedirectedOutputLevel + virtual void setStreamingForRedirectedOutputLevel(RedirectionLevel redirectionLevel) override; + + //! Process event, cross thread messages + virtual bool event(QEvent *event) override; + + //! Reset output redirection + static void resetOutputRedirection(); + + protected: + //! Constructor + CContextApplicationBase(CRuntimeConfig::ContextMode mode, CRuntime *runtime); + + private: + //! All contexts, used with messageHandler + static QList s_contexts; + + //! Previous message handler + static QtMessageHandler s_oldHandler; + + //! Message handler, handles one individual context + void messageHandler(QtMsgType type, const QMessageLogContext &messageContext, const QString &messsage); + + //! Handle output dispatch, handles all contexts + static void messageHandlerDispatch(QtMsgType type, const QMessageLogContext &messageContext, const QString &message); + + RedirectionLevel m_outputRedirectionLevel; //!< enable / disable my output + RedirectionLevel m_redirectedOutputRedirectionLevel; //!< enable / disable others output + + private slots: + //! Re-stream the redirected output + void streamRedirectedOutput(const BlackMisc::CStatusMessage &message, qint64 contextId); + }; + + /*! + * \brief Event to allow cross thread output redirection + */ + class CApplicationEvent : public QEvent + { + friend class CContextApplicationBase; + + public: + //! Constructor + CApplicationEvent(const BlackMisc::CStatusMessage &msg, qint64 contextId) : + QEvent(eventType()), m_message(msg), m_contextId(contextId) {} + //! Destructor + virtual ~CApplicationEvent() {} + //! Event type + static const QEvent::Type &eventType() + { + const static QEvent::Type t = static_cast(QEvent::registerEventType()); + return t; + } + + private: + BlackMisc::CStatusMessage m_message; + qint64 m_contextId; + }; + +} // namespace + +#endif // guard diff --git a/src/blackcore/context_application_impl.cpp b/src/blackcore/context_application_impl.cpp index d46066c5c..569ebc35d 100644 --- a/src/blackcore/context_application_impl.cpp +++ b/src/blackcore/context_application_impl.cpp @@ -15,10 +15,9 @@ namespace BlackCore /* * Init this context */ - CContextApplication::CContextApplication(CRuntimeConfig::ContextMode mode, CRuntime *runtime) : IContextApplication(mode, runtime) - { - // void - } + CContextApplication::CContextApplication(CRuntimeConfig::ContextMode mode, CRuntime *runtime) : + CContextApplicationBase(mode, runtime) + {} /* * Ping, is DBus alive? diff --git a/src/blackcore/context_application_impl.h b/src/blackcore/context_application_impl.h index 98cf65d8c..034cccc72 100644 --- a/src/blackcore/context_application_impl.h +++ b/src/blackcore/context_application_impl.h @@ -17,7 +17,7 @@ namespace BlackCore /*! * \brief Application context */ - class CContextApplication : public IContextApplication + class CContextApplication : public CContextApplicationBase { Q_CLASSINFO("D-Bus Interface", BLACKCORE_CONTEXTAPPLICATION_INTERFACENAME) Q_OBJECT diff --git a/src/blackcore/context_application_proxy.cpp b/src/blackcore/context_application_proxy.cpp index 593f5fa22..bb05f9c75 100644 --- a/src/blackcore/context_application_proxy.cpp +++ b/src/blackcore/context_application_proxy.cpp @@ -15,7 +15,7 @@ namespace BlackCore /* * Constructor for DBus */ - CContextApplicationProxy::CContextApplicationProxy(const QString &serviceName, QDBusConnection &connection, CRuntimeConfig::ContextMode mode, CRuntime *runtime) : IContextApplication(mode, runtime), m_dBusInterface(nullptr) + CContextApplicationProxy::CContextApplicationProxy(const QString &serviceName, QDBusConnection &connection, CRuntimeConfig::ContextMode mode, CRuntime *runtime) : CContextApplicationBase(mode, runtime), m_dBusInterface(nullptr) { this->m_dBusInterface = new BlackMisc::CGenericDBusInterface(serviceName , IContextApplication::ObjectPath(), IContextApplication::InterfaceName(), connection, this); this->relaySignals(serviceName, connection); diff --git a/src/blackcore/context_application_proxy.h b/src/blackcore/context_application_proxy.h index 435172f87..bb69b7b46 100644 --- a/src/blackcore/context_application_proxy.h +++ b/src/blackcore/context_application_proxy.h @@ -15,7 +15,7 @@ namespace BlackCore /*! * \brief Application context proxy */ - class CContextApplicationProxy : public IContextApplication + class CContextApplicationProxy : public CContextApplicationBase { Q_OBJECT friend class CRuntime; @@ -33,7 +33,7 @@ namespace BlackCore protected: //! Constructor - CContextApplicationProxy(CRuntimeConfig::ContextMode mode, CRuntime *runtime) : IContextApplication(mode, runtime), m_dBusInterface(nullptr) {} + CContextApplicationProxy(CRuntimeConfig::ContextMode mode, CRuntime *runtime) : CContextApplicationBase(mode, runtime), m_dBusInterface(nullptr) {} //! DBus version constructor CContextApplicationProxy(const QString &serviceName, QDBusConnection &connection, CRuntimeConfig::ContextMode mode, CRuntime *runtime);