#acl FriedemannBunjes:read,write,delete,revert All:read #format wiki #language en #pragma section-numbers 2 = Understanding the Logger = <> The Logger is used to transfer messages between different threads running at different priority. To illustrate how the logger mechanism works, its structure is explained and it is shown how a new !LogReader is implemented. == The macros == The ERROR, INFO, DEBUG or MSGBOX macros are defined in CException.h. They wrap calls to methods of the Logger singleton: {{{ #define DEBUG( MSG, ... ) \ CLogger::GetLogger() \ ->Debug( MSG, __FILE__, __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__ ) }}} == The Logger and the LogQueue == In CLogger, these methods create a CLogMessage object which is added to the logger queue. Via CLogger::!RegisterLogReader !LogReader objects can be registered at the logger queue. Registering the first !LogReader starts the logger thread. CLogQueue::Notify continuously pushes the first message in the queue to all registered !LogReaders by calling their Update method: {{{ void CLogQueue::Execute() { // Loop until someone Stop()s the logger thread. while( ! CThread::StopRequested() ) { ... // Let the LogReaders output all pending messages while( MessagesPending() ) { Notify(); } } } ... void CLogQueue::Notify() { ... // Send message to all registered LogReaders CAutoLocker oLocker( m_poReadersLock ); if ( ! m_lstLogReaders.empty() ) { std::list< ILogReader* >::iterator itLogReader; for( itLogReader = m_lstLogReaders.begin(); itLogReader != m_lstLogReaders.end(); ++itLogReader ) { (*itLogReader)->Update( oLogMessage ); } } oLocker.Unlock( m_poReadersLock ); ... } }}} == Implementing a new LogReader == A !LogReader that captures log messages to display them as messagebox dialogs to the user is implemented. First, a new macro is added to CException.h: {{{ #define MSGBOX( MSG, ... ) \ CLogger::GetLogger() \ ->MessageBox( MSG, __FILE__, __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__ ) }}} And it's corresponding method to CLogger (CLogger.h): {{{ ... LOG_MSGBOX = 1 << 5 ... void MessageBox( const std::string& sMessage, const std::string& sFile = "", int iLine = 0, const std::string& sFunc = "", ... ); }}} Then a new !LogReader class is created which inherits ILogReader and (for the user interface connection) QObject: {{{ class CLogReaderGui : QObject, public ILogReader { public: CLogReaderGui(); ~CLogReaderGui(); /// ILogReader implementation void ShowLog( const CLogMessage& oLogMessage ); /* * Process messages. Called by CGuiMain::Idle() */ void ProcessMessages(); protected: void ShowMessageBox( const CLogMessage& oLogMessage ); /* * Queue to collect gui logging messages into. */ std::vector< CLogMessage > m_vecLogMessageQueue; /* * Mutex to lock the queue vector. */ CMutex* m_poQueueLock; }; }}} In the !ShowLog method, which is called in ILogReader::Update after checking the !CategoryMask (is this type of log activated?), the sent message is added to the message queue. !ProcessMessages is called in the CGuiMain::Idle method, which processes recurrent user interface events: {{{ // process events if( m_poLogReaderGui != NULL ) m_poLogReaderGui->ProcessMessages(); }}} To give CGuiMain access to the !LogReader, it is created in the constructor of CGuiMain (and deleted in the destructor): {{{ // Create LogReaderGui m_poLogReaderGui = new CLogReaderGui; // We do want messageboxes for user warnings m_poLogReaderGui->SetCategory( CLogger::LOG_MSGBOX ); // register it at the logger CLogger::GetLogger()->RegisterLogReader( m_poLogReaderGui ); }}} In CLogReaderGui, it is important, to lock all access to the queue by a mutex. Activate the locker as shortly as possible: {{{ CLogReaderGui.h: ... /* * Mutex to lock the queue vector. */ CMutex* m_poQueueLock; ... CLogReaderGui.cpp: ... /* * Process messages. Called by CGuiMain::Idle() */ void CLogReaderGui::ProcessMessages() { TRY // lock queue list while reading out and emptying it CAutoLocker oLocker( m_poQueueLock ); while( ! m_vecLogMessageQueue.empty() ) { CLogMessage oLogMessage = m_vecLogMessageQueue.front(); m_vecLogMessageQueue.erase( m_vecLogMessageQueue.begin() ); oLocker.Unlock( m_poQueueLock ); ShowMessageBox( oLogMessage ); oLocker.Lock( m_poQueueLock ); } oLocker.Unlock( m_poQueueLock ); CATCHALL } }}} And finally, here is the implementation of the !MessageBox call: {{{ void CLogReaderGui::ShowMessageBox( const CLogMessage& oLogMessage ) { TRY QMessageBox* msgBox = new QMessageBox( ); msgBox->setStandardButtons( QMessageBox::Ok ); std::string sTitle = "Warning"; msgBox->setWindowTitle( sTitle.c_str() ); std::string sMessage = oLogMessage.GetMessage() + "\n\nWarning in\n" + oLogMessage.GetFunc(); msgBox->setText( sMessage.c_str() ); msgBox->setIcon( QMessageBox::Warning ); msgBox->setModal( false ); msgBox->setAttribute( Qt::WA_DeleteOnClose, true ); msgBox->open( ); CATCHALL } }}}