refs #316 Added CLogHandler which allows to subscribe to log messages.

This commit is contained in:
Mathew Sutcliffe
2014-09-25 19:47:37 +01:00
parent ce6cdf12a7
commit ed723c5e97
2 changed files with 225 additions and 0 deletions

View File

@@ -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 <QCoreApplication>
#include <QMetaMethod>
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<CLogCategoryHandler *> CLogHandler::handlersForCategory(const QString &category) const
{
QList<CLogCategoryHandler *> 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<CLogCategoryHandler *> &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);
}
}
}

101
src/blackmisc/loghandler.h Normal file
View File

@@ -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 <QObject>
#include <QMap>
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<CLogCategoryHandler *> &handlers) const;
QMap<QString, CLogCategoryHandler *> m_categoryHandlers;
QList<CLogCategoryHandler *> 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