Differences between revisions 5 and 6
Revision 5 as of 2012-02-20 08:40:06
Size: 5434
Editor: hih-kn-2301-01
Comment:
Revision 6 as of 2012-02-20 08:42:00
Size: 5424
Editor: hih-kn-2301-01
Comment:
Deletions are marked like this. Additions are marked like this.
Line 23: Line 23:
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:
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:
Line 62: Line 62:
First, a new macro is added to !CException.h: First, a new macro is added to CException.h:
Line 76: Line 76:
Then a new !LogReader class is created which inherits !ILogReader and (for the user interface connection) !QObject: Then a new !LogReader class is created which inherits ILogReader and (for the user interface connection) QObject:
Line 109: Line 109:
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:
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:
Line 117: Line 117:
To give !CGuiMain access to the !LogReader, it is created in the constructor of !CGuiMain (and deleted in the destructor): To give CGuiMain access to the !LogReader, it is created in the constructor of CGuiMain (and deleted in the destructor):
Line 127: Line 127:
In !CLogReaderGui, it is important, to lock all access to the queue by a mutex. Activate the locker as shortly as possible: In CLogReaderGui, it is important, to lock all access to the queue by a mutex. Activate the locker as shortly as possible:

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.

1. 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__ )

2. 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 );
...
}

3. 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
}

nrec: UnderstandingTheLogger (last edited 2012-02-20 08:42:00 by hih-kn-2301-01)