diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.log/documentation/UserManual/LogView.png b/BlueBerry/Bundles/org.blueberry.ui.qt.log/documentation/UserManual/LogView.png index 3719e8e35e..e23a2a63ab 100644 Binary files a/BlueBerry/Bundles/org.blueberry.ui.qt.log/documentation/UserManual/LogView.png and b/BlueBerry/Bundles/org.blueberry.ui.qt.log/documentation/UserManual/LogView.png differ diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.log/documentation/UserManual/LogViewExplain.png b/BlueBerry/Bundles/org.blueberry.ui.qt.log/documentation/UserManual/LogViewExplain.png new file mode 100644 index 0000000000..063c2a6a3f Binary files /dev/null and b/BlueBerry/Bundles/org.blueberry.ui.qt.log/documentation/UserManual/LogViewExplain.png differ diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.log/documentation/UserManual/blueberrylogview.dox b/BlueBerry/Bundles/org.blueberry.ui.qt.log/documentation/UserManual/blueberrylogview.dox index 89c0f0cbc1..fcedac60af 100644 --- a/BlueBerry/Bundles/org.blueberry.ui.qt.log/documentation/UserManual/blueberrylogview.dox +++ b/BlueBerry/Bundles/org.blueberry.ui.qt.log/documentation/UserManual/blueberrylogview.dox @@ -1,8 +1,16 @@ /** \bundlemainpage{org.berry.log} The Logging Module \image html Logging.png "Icon of the Module" -Once activated the Logging Module records all logging output of events and progress as specified in the source code with time of occurence, level of importance (Info, Warning, Error, Fatal, Debug), the message given and where it happens. The filter text field allows for searching all log events containing a certain substring. +The Plug-In "Logging Module" records all logging output of events and progress as specified in the source code with time of occurence, level of importance (Info, Warning, Error, Fatal, Debug), the message given and where it happens. The logging starts once you activate the Plug-In in your main application. A screenshot of the main view of the Logging Module is shown next. + +\image html LogView.png "Screenshot of the Logging Module" + +There are different features available in the view. The filter text field allows for searching all log events containing a certain substring. Using the button "Copy to clipboard" on the bottom right you can copy the current content of the logging view to your clipboard. This enables you to insert the logging information to any text processing application. + +You can also show more information on every logging message by activating the two checkboxes. In the simple view, leaving both checkboxes unchecked, you'll see logging messages and logging levels. A brief description of the logging levels can be found in the \ref LoggingPage "logging concept documentation". The checkbox "Category" adds a column for the category. The checkbox "Show Advanced Field" shows method, filename and linenumber where the logging message was emitted as well as the running time of the application. The next figure shows all information which can be shown in the Logging Module. + +\image html LogViewExplain.png "Details on the Vizualized Logging Information" */ \ No newline at end of file diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtLogView.cpp b/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtLogView.cpp index aba6b896ef..1e25b4c2cc 100644 --- a/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtLogView.cpp +++ b/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtLogView.cpp @@ -1,121 +1,150 @@ /*=================================================================== BlueBerry Platform Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifdef __MINGW32__ // We need to inlclude winbase.h here in order to declare // atomic intrinsics like InterlockedIncrement correctly. // Otherwhise, they would be declared wrong within qatomic_windows.h . #include #endif #include "berryQtLogView.h" #include "berryQtLogPlugin.h" #include #include #include #include #include #include +#include namespace berry { QtLogView::QtLogView(QWidget *parent) : QWidget(parent) { berry::IPreferencesService::Pointer prefService = berry::Platform::GetServiceRegistry() .GetServiceById(berry::IPreferencesService::ID); berry::IBerryPreferences::Pointer prefs = (prefService->GetSystemPreferences()->Node("org_blueberry_ui_qt_log")) .Cast(); - bool showAdvancedFields = - prefs->GetBool("ShowAdvancedFields", true) ; + + prefs->PutBool("ShowAdvancedFields", false); + prefs->PutBool("ShowCategory", true); + bool showAdvancedFields = false; + ui.setupUi(this); model = QtLogPlugin::GetInstance()->GetLogModel(); model->SetShowAdvancedFiels( showAdvancedFields ); filterModel = new QSortFilterProxyModel(this); filterModel->setSourceModel(model); filterModel->setFilterKeyColumn(-1); ui.tableView->setModel(filterModel); ui.tableView->verticalHeader()->setVisible(false); ui.tableView->horizontalHeader()->setStretchLastSection(true); connect( ui.filterContent, SIGNAL( textChanged( const QString& ) ), this, SLOT( slotFilterChange( const QString& ) ) ); connect( filterModel, SIGNAL( rowsInserted ( const QModelIndex &, int, int ) ), this, SLOT( slotRowAdded( const QModelIndex &, int , int ) ) ); + connect( ui.ShowCategory, SIGNAL( clicked(bool checked)),this, SLOT(on_ShowAdvancedFields_clicked(checked))); + connect( ui.SaveToClipboard, SIGNAL( clicked()),this, SLOT(on_SaveToClipboard_clicked())); + ui.ShowAdvancedFields->setChecked( showAdvancedFields ); } QtLogView::~QtLogView() { } void QtLogView::slotScrollDown( ) { ui.tableView->scrollToBottom(); } void QtLogView::slotFilterChange( const QString& q ) { filterModel->setFilterRegExp(QRegExp(q, Qt::CaseInsensitive, QRegExp::FixedString)); } void QtLogView::slotRowAdded ( const QModelIndex & /*parent*/, int start, int end ) { static int first=false; if(!first) { first=true; ui.tableView->resizeColumnsToContents(); ui.tableView->resizeRowsToContents(); } else for(int r=start;r<=end;r++) { ui.tableView->resizeRowToContents(r); } QTimer::singleShot(0,this,SLOT( slotScrollDown() ) ); } void QtLogView::on_ShowAdvancedFields_clicked( bool checked ) { QtLogPlugin::GetInstance()->GetLogModel()->SetShowAdvancedFiels( checked ); ui.tableView->resizeColumnsToContents(); berry::IPreferencesService::Pointer prefService = berry::Platform::GetServiceRegistry() .GetServiceById(berry::IPreferencesService::ID); berry::IBerryPreferences::Pointer prefs = (prefService->GetSystemPreferences()->Node("org_blueberry_ui_qt_log")) .Cast(); prefs->PutBool("ShowAdvancedFields", checked); prefs->Flush(); } +void QtLogView::on_ShowCategory_clicked( bool checked ) +{ + QtLogPlugin::GetInstance()->GetLogModel()->SetShowCategory( checked ); + ui.tableView->resizeColumnsToContents(); + + berry::IPreferencesService::Pointer prefService + = berry::Platform::GetServiceRegistry() + .GetServiceById(berry::IPreferencesService::ID); + berry::IBerryPreferences::Pointer prefs + = (prefService->GetSystemPreferences()->Node("org_blueberry_ui_qt_log")) + .Cast(); + + prefs->PutBool("ShowCategory", checked); + prefs->Flush(); +} + +void QtLogView::on_SaveToClipboard_clicked() +{ + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(model->GetDataAsString()); +} + } diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtLogView.h b/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtLogView.h index 2cc9eded9b..7b259e4bed 100644 --- a/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtLogView.h +++ b/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtLogView.h @@ -1,52 +1,54 @@ /*=================================================================== BlueBerry Platform Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef BERRYQTLOGVIEW_H #define BERRYQTLOGVIEW_H #include #include #include "ui_berryQtLogView.h" #include "berryQtPlatformLogModel.h" namespace berry { class QtLogView : public QWidget { Q_OBJECT public: QtLogView(QWidget *parent = 0); ~QtLogView(); QtPlatformLogModel *model; QSortFilterProxyModel *filterModel; private: Ui::QtLogViewClass ui; protected slots: void slotFilterChange( const QString& ); void slotRowAdded( const QModelIndex & , int , int ); void slotScrollDown( ); void on_ShowAdvancedFields_clicked( bool checked = false ); + void on_ShowCategory_clicked( bool checked = false ); + void on_SaveToClipboard_clicked(); }; } #endif // BERRYQTLOGVIEW_H diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtLogView.ui b/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtLogView.ui index 3f68e4266f..594cb1aa9b 100644 --- a/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtLogView.ui +++ b/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtLogView.ui @@ -1,137 +1,201 @@ QtLogViewClass 0 0 - 415 - 496 + 596 + 309 0 0 QtLogView - - - 2 - + - + - + - - - - 0 - 0 - - - - Filter: - - + + + + + + 0 + 0 + + + + Filter: + + + + + + + - + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + Show advanced fields + + false + + + + + + + Show category + true + + + + + + + 0 + 0 + + + + + 8 + + + + Qt::ScrollBarAlwaysOn + + + false + + + false + + + false + + + QAbstractItemView::NoDragDrop + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + Qt::ElideLeft + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + false + + + Qt::DashLine + + + false + + + false + + + 64 + + + 0 + + + false + + + 16 + + + false + + + 8 + + + + + - - - - 0 - 0 - - - - - 8 - - - - Qt::ScrollBarAlwaysOn - - - false - - - false - - - true + + + Qt::Horizontal - - QAbstractItemView::NoSelection + + + 40 + 20 + - - QAbstractItemView::SelectRows - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - false - - - Qt::DashLine - - - false - - - false + + + + + + Copy to clipboard - - 64 - - - 0 - - - false - - - 16 - - - false - - - 8 - diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtPlatformLogModel.cpp b/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtPlatformLogModel.cpp index 7c9c29c629..50d44e3585 100644 --- a/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtPlatformLogModel.cpp +++ b/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtPlatformLogModel.cpp @@ -1,330 +1,329 @@ /*=================================================================== BlueBerry Platform Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifdef __MINGW32__ // We need to inlclude winbase.h here in order to declare // atomic intrinsics like InterlockedIncrement correctly. // Otherwhise, they would be declared wrong within qatomic_windows.h . #include #endif #include "berryQtPlatformLogModel.h" #include "berryPlatform.h" #include "event/berryPlatformEvents.h" #include #include #include #include #include #include "berryLog.h" #include #include +#include namespace berry { const QString QtPlatformLogModel::Error = QString("Error"); const QString QtPlatformLogModel::Warn = QString("Warning"); const QString QtPlatformLogModel::Fatal = QString("Fatal"); const QString QtPlatformLogModel::Info = QString("Info"); const QString QtPlatformLogModel::Debug = QString("Debug"); void QtPlatformLogModel::slotFlushLogEntries() { m_Mutex.lock(); std::list *tmp=m_Active; m_Active=m_Pending; m_Pending=tmp; m_Mutex.unlock(); int num = static_cast(m_Pending->size()); if (num > 0) { int row = static_cast(m_Entries.size()); this->beginInsertRows(QModelIndex(), row, row+num-1); do { m_Entries.push_back(m_Pending->front()); m_Pending->pop_front(); + + + } while(--num); this->endInsertRows(); } } void QtPlatformLogModel::addLogEntry(const mbilog::LogMessage &msg) { m_Mutex.lock(); //mbilog::BackendCout::FormatSmart(msg); FormatSmart is not static any more. So commented out this statement. Todo: fix m_Active->push_back(ExtendedLogMessage(msg)); m_Mutex.unlock(); emit signalFlushLogEntries(); + } void QtPlatformLogModel::SetShowAdvancedFiels( bool showAdvancedFiels ) { if( m_ShowAdvancedFiels != showAdvancedFiels ) { m_ShowAdvancedFiels = showAdvancedFiels; this->reset(); + + } +} + +void QtPlatformLogModel::SetShowCategory( bool showCategory ) +{ + if( m_ShowCategory != showCategory ) + { + m_ShowCategory = showCategory; + this->reset(); } } void QtPlatformLogModel::addLogEntry(const PlatformEvent& event) { const Poco::Message& entry = Poco::RefAnyCast(*event.GetData()); mbilog::LogMessage msg(mbilog::Info,"n/a",-1,"n/a"); msg.message += entry.getText(); msg.category = "BlueBerry."+entry.getSource(); msg.moduleName = "n/a"; addLogEntry(msg); } -QtPlatformLogModel::QtPlatformLogModel(QObject* parent) : QAbstractTableModel(parent), m_ShowAdvancedFiels(true) +QtPlatformLogModel::QtPlatformLogModel(QObject* parent) : QAbstractTableModel(parent), +m_ShowAdvancedFiels(false), +m_ShowCategory(true) { m_Active=new std::list; m_Pending=new std::list; connect(this, SIGNAL(signalFlushLogEntries()), this, SLOT( slotFlushLogEntries() ), Qt::QueuedConnection ); Platform::GetEvents().logged += PlatformEventDelegate(this, &QtPlatformLogModel::addLogEntry); myBackend = new QtLogBackend(this); } QtPlatformLogModel::~QtPlatformLogModel() { disconnect(this, SIGNAL(signalFlushLogEntries()), this, SLOT( slotFlushLogEntries() )); // dont delete and unregister backend, only deactivate it to avoid thread syncronization issues cause mbilog::UnregisterBackend is not threadsafe // will be fixed. // delete myBackend; // delete m_Active; // delete m_Pending; m_Mutex.lock(); myBackend->Deactivate(); m_Mutex.unlock(); } // QT Binding int QtPlatformLogModel::rowCount(const QModelIndex&) const { return static_cast(m_Entries.size()); } int QtPlatformLogModel::columnCount(const QModelIndex&) const { - if( m_ShowAdvancedFiels ) - return 8; - else - return 2; + int returnValue = 2; + if( m_ShowAdvancedFiels ) returnValue += 7; + if( m_ShowCategory ) returnValue += 1; + return returnValue; } /* struct LogEntry { LogEntry(const std::string& msg, const std::string& src, std::time_t t) : message(msg.c_str()), moduleName(src.c_str()),time(std::clock()) { } QString message; clock_t time; QString level; QString filePath; QString lineNumber; QString moduleName; QString category; QString function; LogEntry(const mbilog::LogMessage &msg) { message = msg.message.c_str(); filePath = msg.filePath; std::stringstream out; out << msg.lineNumber; lineNumber = out.str().c_str(); moduleName = msg.moduleName; category = msg.category.c_str(); function = msg.functionName; time=std::clock(); } }; */ -QVariant -QtPlatformLogModel::data(const QModelIndex& index, int role) const +QVariant QtPlatformLogModel::data(const QModelIndex& index, int role) const { - const ExtendedLogMessage *msg = &m_Entries[index.row()]; + const ExtendedLogMessage *msg = &m_Entries[index.row()]; + if (role == Qt::DisplayRole) - { - if( m_ShowAdvancedFiels ) { - switch (index.column()) { - - case 0: { - std::stringstream ss; - std::locale C("C"); - ss.imbue(C); - ss << std::setw(7) << std::setprecision(3) << std::fixed << ((double)msg->time)/CLOCKS_PER_SEC; - return QVariant(QString(ss.str().c_str())); - } - - case 1: - { - // change muellerm, an icon is returned do not return text - switch(msg->message.level) - { - default: - case mbilog::Info: - return QVariant(Info); - - case mbilog::Warn: - return QVariant(Warn); - - case mbilog::Error: - return QVariant(Error); - - case mbilog::Fatal: - return QVariant(Fatal); - - case mbilog::Debug: - return QVariant(Debug); - } - } - - case 2: - return QVariant(QString(msg->message.message.c_str())); - - case 3: - return QVariant(QString(msg->message.category.c_str())); - - case 4: - return QVariant(QString(msg->message.moduleName)); - - case 5: - return QVariant(QString(msg->message.functionName)); - - case 6: - return QVariant(QString(msg->message.filePath)); - - case 7: - { - std::stringstream out; - std::locale C("C"); - out.imbue(C); - out << msg->message.lineNumber; - return QVariant(QString(out.str().c_str())); - } - } - } - else // m_ShowAdvancedFields - { - // only return text - if( index.column() == 0 ) + switch (index.column()) { - switch(msg->message.level) - { - default: - case mbilog::Info: - return QVariant(Info); - - case mbilog::Warn: - return QVariant(Warn); - - case mbilog::Error: - return QVariant(Error); - - case mbilog::Fatal: - return QVariant(Fatal); - - case mbilog::Debug: - return QVariant(Debug); - } - } - if( index.column()==1 ) - { - return QVariant(QString(msg->message.message.c_str())); + case 0: + if (m_ShowAdvancedFiels) return msg->getTime(); + else return msg->getLevel(); + case 1: + if (m_ShowAdvancedFiels) return msg->getLevel(); + else return msg->getMessage(); + case 2: + if (m_ShowAdvancedFiels) return msg->getMessage(); + else return msg->getCategory(); + case 3: + if (m_ShowAdvancedFiels && m_ShowCategory) return msg->getCategory(); + else if (m_ShowAdvancedFiels && !m_ShowCategory) return msg->getModuleName(); + else break; + case 4: + if (m_ShowAdvancedFiels && m_ShowCategory) return msg->getModuleName(); + else if (m_ShowAdvancedFiels && !m_ShowCategory) return msg->getFunctionName(); + else break; + case 5: + if (m_ShowAdvancedFiels && m_ShowCategory) return msg->getFunctionName(); + else if (m_ShowAdvancedFiels && !m_ShowCategory) return msg->getPath(); + else break; + case 6: + if (m_ShowAdvancedFiels && m_ShowCategory) return msg->getPath(); + else if (m_ShowAdvancedFiels && !m_ShowCategory) return msg->getLine(); + else break; + case 7: + if (m_ShowAdvancedFiels && m_ShowCategory) return msg->getLine(); + else break; } } - } + else if( role == Qt::DecorationRole ) { if ( (m_ShowAdvancedFiels && index.column()==1) || (!m_ShowAdvancedFiels && index.column()==0) ) { QString file ( ":/org_blueberry_ui_qt_log/information.png" ); if( msg->message.level == mbilog::Error ) file = ":/org_blueberry_ui_qt_log/error.png"; else if( msg->message.level == mbilog::Warn ) file = ":/org_blueberry_ui_qt_log/warning.png"; else if( msg->message.level == mbilog::Debug ) file = ":/org_blueberry_ui_qt_log/debug.png"; else if( msg->message.level == mbilog::Fatal ) file = ":/org_blueberry_ui_qt_log/fatal.png"; QIcon icon(file); return QVariant(icon); } } return QVariant(); } QVariant QtPlatformLogModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { - if( m_ShowAdvancedFiels ) + if( m_ShowAdvancedFiels && m_ShowCategory ) { switch (section) { case 0: return QVariant("Time"); case 1: return QVariant("Level"); case 2: return QVariant("Message"); case 3: return QVariant("Category"); case 4: return QVariant("Module"); case 5: return QVariant("Function"); case 6: return QVariant("File"); case 7: return QVariant("Line"); } } - else + else if (m_ShowAdvancedFiels && !m_ShowCategory) + { + switch (section) + { + case 0: return QVariant("Time"); + case 1: return QVariant("Level"); + case 2: return QVariant("Message"); + case 3: return QVariant("Module"); + case 4: return QVariant("Function"); + case 5: return QVariant("File"); + case 6: return QVariant("Line"); + } + } + else //!m_ShowAdvancedFiels, m_ShowCategory is not handled seperately because it only activates case 2 { switch (section) { case 0: return QVariant("Severtiy"); case 1: return QVariant("Message"); + case 2: return QVariant("Category"); } } } return QVariant(); } +QVariant QtPlatformLogModel::ExtendedLogMessage::getTime() const + { + std::stringstream ss; + std::locale C("C"); + ss.imbue(C); + ss << std::setw(7) << std::setprecision(3) << std::fixed << ((double)this->time)/CLOCKS_PER_SEC; + return QVariant(QString(ss.str().c_str())); + } + +QString QtPlatformLogModel::GetDataAsString() + { + QString returnValue(""); + + for (int message=0; messagerowCount(QModelIndex()); message++) + { + for (int column=0; columncolumnCount(QModelIndex()); column++) + { + returnValue += " " + this->data(this->index(message,column),Qt::DisplayRole).toString(); + } + returnValue += "\n"; + } + + return returnValue; + } + } diff --git a/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtPlatformLogModel.h b/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtPlatformLogModel.h index c10e881d30..a8453ba339 100644 --- a/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtPlatformLogModel.h +++ b/BlueBerry/Bundles/org.blueberry.ui.qt.log/src/internal/berryQtPlatformLogModel.h @@ -1,140 +1,221 @@ /*=================================================================== BlueBerry Platform Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef BERRYQTPLATFORMLOGMODEL_H_ #define BERRYQTPLATFORMLOGMODEL_H_ #include "berryLog.h" #include #include #include "event/berryPlatformEvent.h" #include "berryMessage.h" #include #include #include #include "berryLog.h" #include namespace berry { +/** Documentation + * @brief An object of this class represents a table of logging data. + * The table presentation can be modified by the methods + * SetShowAdvancedFiels() and SetShowCategory(). + */ class QtPlatformLogModel : public QAbstractTableModel { Q_OBJECT public: QtPlatformLogModel(QObject* parent = 0); ~QtPlatformLogModel(); void SetShowAdvancedFiels( bool showAdvancedFiels ); + void SetShowCategory( bool showCategory ); int rowCount(const QModelIndex&) const; int columnCount(const QModelIndex&) const; QVariant data(const QModelIndex& index, int) const; + /** Documentation + * @return Retruns the complete table data as string representation. + */ + QString GetDataAsString(); + QVariant headerData(int section, Qt::Orientation orientation, int) const; void addLogEntry(const mbilog::LogMessage &msg); void addLogEntry(const PlatformEvent& event); private: bool m_ShowAdvancedFiels; - - typedef MessageDelegate1 PlatformEventDelegate; - + bool m_ShowCategory; + + /** Documentation + * @brief An object of this struct internally represents a logging message. + * It offers methods to convert the logging data into QVaraint objects + * and also adds time and threadid as logging data. The struct is + * internally used to store logging data in the table data model. + */ struct ExtendedLogMessage { mbilog::LogMessage message; clock_t time; int threadid; ExtendedLogMessage(const ExtendedLogMessage &src):message(src.message),time(src.time),threadid(src.threadid) { } ExtendedLogMessage(const mbilog::LogMessage &msg):message(msg),time(std::clock()),threadid(0) { } ExtendedLogMessage operator = (const ExtendedLogMessage& src) { return ExtendedLogMessage(src); } + QVariant getLevel() const + { + switch(this->message.level) + { + default: + case mbilog::Info: + return QVariant(Info); + + case mbilog::Warn: + return QVariant(Warn); + + case mbilog::Error: + return QVariant(Error); + + case mbilog::Fatal: + return QVariant(Fatal); + + case mbilog::Debug: + return QVariant(Debug); + } + } + + QVariant getMessage() const + { + return QVariant(QString(this->message.message.c_str())); + } + + QVariant getCategory() const + { + return QVariant(QString(this->message.category.c_str())); + } + + QVariant getModuleName() const + { + return QVariant(QString(this->message.moduleName)); + } + + QVariant getFunctionName() const + { + return QVariant(QString(this->message.functionName)); + } + + QVariant getPath() const + { + return QVariant(QString(this->message.filePath)); + } + + QVariant getLine() const + { + std::stringstream out; + std::locale C("C"); + out.imbue(C); + out << this->message.lineNumber; + return QVariant(QString(out.str().c_str())); + } + + /** This method is implemented in the cpp file to save includes. */ + QVariant getTime() const; + }; + + + + typedef MessageDelegate1 PlatformEventDelegate; + + class QtLogBackend : public mbilog::BackendBase { public: QtLogBackend(QtPlatformLogModel *_myModel) { myModel=_myModel; deactivated = false; mbilog::RegisterBackend(this); BERRY_INFO << "BlueBerry mbilog backend registered"; } ~QtLogBackend() { mbilog::UnregisterBackend(this); } void ProcessMessage(const mbilog::LogMessage &l ) { if(!deactivated) myModel->addLogEntry(l); } void Deactivate() { deactivated=true; } private: QtPlatformLogModel *myModel; bool deactivated; } *myBackend; std::vector m_Entries; std::list *m_Active,*m_Pending; static const QString Error; static const QString Warn; static const QString Fatal; static const QString Info; static const QString Debug; QMutex m_Mutex; signals: void signalFlushLogEntries(); protected slots: void slotFlushLogEntries(); }; } #endif /*BERRYQTPLATFORMLOGMODEL_H_*/ diff --git a/BlueBerry/CMakeLists.txt b/BlueBerry/CMakeLists.txt index 4be3b99ef6..01c16652c0 100644 --- a/BlueBerry/CMakeLists.txt +++ b/BlueBerry/CMakeLists.txt @@ -1,276 +1,276 @@ project(BlueBerry) cmake_minimum_required(VERSION 2.8.4) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/CMake/") include(MacroParseArguments) include(MacroConvertSchema) include(MacroOrganizeSources) include(MacroCreateCTKPlugin) include(MacroCreateQtHelp) include(MacroInstallCTKPlugin) include(FunctionCreateProvisioningFile) if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4250 /wd4275 /wd4251 /wd4503") endif() if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) endif() find_package(mbilog REQUIRED) include_directories(${mbilog_INCLUDE_DIRS}) find_package(Qt4 4.6.2 REQUIRED) if(QT_QMAKE_CHANGED) set(QT_HELPGENERATOR_EXECUTABLE NOTFOUND) set(QT_COLLECTIONGENERATOR_EXECUTABLE NOTFOUND) set(QT_ASSISTANT_EXECUTABLE NOTFOUND) set(QT_XMLPATTERNS_EXECUTABLE NOTFOUND) endif() find_program(QT_HELPGENERATOR_EXECUTABLE NAMES qhelpgenerator qhelpgenerator-qt4 qhelpgenerator4 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_COLLECTIONGENERATOR_EXECUTABLE NAMES qcollectiongenerator qcollectiongenerator-qt4 qcollectiongenerator4 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_ASSISTANT_EXECUTABLE NAMES assistant-qt4 assistant4 assistant PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_XMLPATTERNS_EXECUTABLE NAMES xmlpatterns PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) option(BLUEBERRY_USE_QT_HELP "Enable support for integrating bundle documentation into Qt Help" ON) mark_as_advanced(BLUEBERRY_USE_QT_HELP QT_HELPGENERATOR_EXECUTABLE QT_COLLECTIONGENERATOR_EXECUTABLE QT_ASSISTANT_EXECUTABLE QT_XMLPATTERNS_EXECUTABLE) set(_doxygen_too_old 1) if(BLUEBERRY_USE_QT_HELP) find_package(Doxygen) if(DOXYGEN_FOUND) execute_process(COMMAND ${DOXYGEN_EXECUTABLE} --version OUTPUT_VARIABLE _doxygen_version) if(${_doxygen_version} VERSION_GREATER 1.6.0 OR ${_doxygen_version} VERSION_EQUAL 1.6.0) set(_doxygen_too_old 0) endif() endif() if(_doxygen_too_old) message("Doxygen was not found or is too old. Version 1.6.0 or later is needed if BLUEBERRY_USE_QT_HELP is ON") set(BLUEBERRY_USE_QT_HELP OFF CACHE BOOL "Enable support for integrating bundle documentation into Qt Help" FORCE) endif() if(NOT QT_HELPGENERATOR_EXECUTABLE) message("You have enabled Qt Help support, but QT_HELPGENERATOR_EXECUTABLE is empty") set(BLUEBERRY_USE_QT_HELP OFF CACHE BOOL "Enable support for integrating bundle documentation into Qt Help" FORCE) endif() if(NOT QT_XMLPATTERNS_EXECUTABLE) message("You have enabled Qt Help support, but QT_XMLPATTERNS_EXECUTABLE is empty") set(BLUEBERRY_USE_QT_HELP OFF CACHE BOOL "Enable support for integrating bundle documentation into Qt Help" FORCE) endif() endif(BLUEBERRY_USE_QT_HELP) include(${QT_USE_FILE}) # ========= CTK specific CMake stuff ============ cmake_policy(SET CMP0012 NEW) find_package(CTK REQUIRED) # Extract all library names starting with org_blueberry_ macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin "^org_blueberry_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin OUTPUT_VARIABLE ${varname}) endmacro() # ================================================ option(BLUEBERRY_BUILD_ALL_PLUGINS "Build all BlueBerry plugins (overriding selection)" OFF) mark_as_advanced(BLUEBERRY_BUILD_ALL_PLUGINS) if(BLUEBERRY_BUILD_ALL_PLUGINS) set(BLUEBERRY_BUILD_ALL_PLUGINS_OPTION "FORCE_BUILD_ALL") endif() option(BLUEBERRY_STATIC "Build all plugins as static libraries" OFF) mark_as_advanced(BLUEBERRY_STATIC) option(BLUEBERRY_DEBUG_SMARTPOINTER "Enable code for debugging smart pointers" OFF) mark_as_advanced(BLUEBERRY_DEBUG_SMARTPOINTER) find_package(Poco REQUIRED) find_package(Ant) find_package(Eclipse) set(BLUEBERRY_SOURCE_DIR ${BlueBerry_SOURCE_DIR}) set(BLUEBERRY_BINARY_DIR ${BlueBerry_BINARY_DIR}) set(BLUEBERRY_PLUGINS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Bundles) set(BLUEBERRY_PLUGINS_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/Bundles) set(OSGI_APP solstice) set(OSGI_UI_APP solstice_ui) if(Eclipse_DIR) set(BLUEBERRY_DOC_TOOLS_DIR "${Eclipse_DIR}" CACHE PATH "Directory containing additional tools needed for generating the documentation") else() set(BLUEBERRY_DOC_TOOLS_DIR "" CACHE PATH "Directory containing additional tools needed for generating the documentation") endif() set(BLUEBERRY_DEBUG_POSTFIX d) # Testing options if(DEFINED BLUEBERRY_BUILD_TESTING) option(BLUEBERRY_BUILD_TESTING "Build the BlueBerry tests." ${BLUEBERRY_BUILD_TESTING}) else() option(BLUEBERRY_BUILD_TESTING "Build the BlueBerry tests." ${BUILD_TESTING}) endif() if(WIN32) set(_gui_testing_default "ON") else() set(_gui_testing_default "OFF") endif() option(BLUEBERRY_ENABLE_GUI_TESTING "Enable the BlueBerry GUI tests" ${_gui_testing_default}) mark_as_advanced(BLUEBERRY_ENABLE_GUI_TESTING) if(BLUEBERRY_BUILD_TESTING) enable_testing() endif() # Add CTK plugins set(_ctk_plugins Bundles/org.blueberry.osgi:ON Bundles/org.blueberry.compat:OFF Bundles/org.blueberry.core.runtime:OFF Bundles/org.blueberry.core.expressions:OFF Bundles/org.blueberry.solstice.common:OFF Bundles/org.blueberry.core.commands:OFF Bundles/org.blueberry.core.jobs:OFF Bundles/org.blueberry.ui:OFF Bundles/org.blueberry.ui.qt:OFF Bundles/org.blueberry.ui.qt.help:OFF - Bundles/org.blueberry.ui.qt.log:OFF + Bundles/org.blueberry.ui.qt.log:ON Bundles/org.blueberry.ui.qt.objectinspector:OFF ) set(_ctk_test_plugins ) set(_ctk_plugins_include_dirs ${Poco_INCLUDE_DIRS} ) set(_ctk_plugins_link_dirs ${Poco_LIBRARY_DIR} ) include_directories(${_ctk_plugins_include_dirs}) link_directories(${_ctk_plugins_link_dirs}) if(BLUEBERRY_BUILD_TESTING) include(berryTestingHelpers) set(BLUEBERRY_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${OSGI_APP}") get_target_property(_is_macosx_bundle ${OSGI_APP} MACOSX_BUNDLE) if(APPLE AND _is_macosx_bundle) set(BLUEBERRY_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${OSGI_APP}.app/Contents/MacOS/${OSGI_APP}") endif() set(_ctk_testinfrastructure_plugins Bundles/org.blueberry.test:ON Bundles/org.blueberry.uitest:ON ) set(_ctk_test_plugins # Testing/org.blueberry.core.runtime.tests:ON # Testing/org.blueberry.osgi.tests:ON ) if(BLUEBERRY_ENABLE_GUI_TESTING) # list(APPEND _ctk_test_plugins Testing/org.blueberry.ui.tests:ON) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${OSGI_UI_APP}") get_target_property(_is_macosx_bundle ${OSGI_UI_APP} MACOSX_BUNDLE) if(APPLE AND _is_macosx_bundle) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${OSGI_UI_APP}.app/Contents/MacOS/${OSGI_UI_APP}") endif() endif() endif() set(BLUEBERRY_TESTING_PROVISIONING_FILE "${BlueBerry_BINARY_DIR}/BlueBerryTesting.provisioning") add_custom_target(BlueBerry) ctkMacroSetupPlugins(${_ctk_plugins} ${_ctk_testinfrastructure_plugins} ${_ctk_test_plugins} BUILD_OPTION_PREFIX BLUEBERRY_BUILD_ BUILD_ALL ${BLUEBERRY_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) set(BLUEBERRY_PROVISIONING_FILE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/BlueBerry.provisioning") FunctionCreateProvisioningFile( FILE ${BLUEBERRY_PROVISIONING_FILE} PLUGINS ${_ctk_plugins} ) FunctionCreateProvisioningFile( FILE ${BLUEBERRY_TESTING_PROVISIONING_FILE} INCLUDE ${BLUEBERRY_PROVISIONING_FILE} PLUGINS ${_ctk_testinfrastructure_plugins} ${_ctk_test_plugins} ) if(${CMAKE_PROJECT_NAME}_PLUGIN_LIBRARIES) add_dependencies(BlueBerry ${${CMAKE_PROJECT_NAME}_PLUGIN_LIBRARIES}) endif() set_property(TARGET ${${CMAKE_PROJECT_NAME}_PLUGIN_LIBRARIES} PROPERTY LABELS BlueBerry) set(BB_PLUGIN_USE_FILE "${BlueBerry_BINARY_DIR}/BlueBerryPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${BB_PLUGIN_USE_FILE}) else() file(REMOVE ${BB_PLUGIN_USE_FILE}) set(BB_PLUGIN_USE_FILE ) endif() # CTK Plugin Exports set(BB_PLUGIN_EXPORTS_FILE "${CMAKE_CURRENT_BINARY_DIR}/BlueBerryPluginExports.cmake") GetMyTargetLibraries("${${PROJECT_NAME}_PLUGIN_LIBRARIES}" my_plugin_targets) set(additional_export_targets mbilog PocoFoundation PocoUtil PocoXML) if(BLUEBERRY_BUILD_TESTING) list(APPEND additional_export_targets CppUnit) endif() export(TARGETS ${my_plugin_targets} ${additional_export_targets} FILE ${BB_PLUGIN_EXPORTS_FILE}) add_subdirectory(Documentation) configure_file(BlueBerryConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/BlueBerryConfig.cmake @ONLY) diff --git a/Core/Code/IO/mitkLog.cpp b/Core/Code/IO/mitkLog.cpp index 0d9c1c9bfb..a56735d8b6 100644 --- a/Core/Code/IO/mitkLog.cpp +++ b/Core/Code/IO/mitkLog.cpp @@ -1,145 +1,152 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLog.h" #include "mitkLogMacros.h" #include "itkSimpleFastMutexLock.h" #include #include #include static itk::SimpleFastMutexLock logMutex; static mitk::LoggingBackend *mitkLogBackend = 0; static std::ofstream *logFile = 0; +static std::string logFileName = ""; static std::stringstream *outputWindow = 0; static bool logOutputWindow = false; void mitk::LoggingBackend::EnableAdditionalConsoleWindow(bool enable) { logOutputWindow = enable; } void mitk::LoggingBackend::ProcessMessage(const mbilog::LogMessage& l ) { logMutex.Lock(); #ifdef _WIN32 FormatSmart( l, (int)GetCurrentThreadId() ); #else FormatSmart( l ); #endif if(logFile) { #ifdef _WIN32 FormatFull( *logFile, l, (int)GetCurrentThreadId() ); #else FormatFull( *logFile, l ); #endif } if(logOutputWindow) { if(outputWindow == NULL) { outputWindow = new std::stringstream();} outputWindow->str(""); outputWindow->clear(); #ifdef _WIN32 FormatFull( *outputWindow, l, (int)GetCurrentThreadId() ); #else FormatFull( *outputWindow, l ); #endif itk::OutputWindow::GetInstance()->DisplayText(outputWindow->str().c_str()); } logMutex.Unlock(); } void mitk::LoggingBackend::Register() { if(mitkLogBackend) return; mitkLogBackend = new mitk::LoggingBackend(); mbilog::RegisterBackend( mitkLogBackend ); } void mitk::LoggingBackend::Unregister() { if(mitkLogBackend) { SetLogFile(0); mbilog::UnregisterBackend( mitkLogBackend ); delete mitkLogBackend; mitkLogBackend=0; } } void mitk::LoggingBackend::SetLogFile(const char *file) { logMutex.Lock(); if(logFile) { - MITK_INFO << "closing logfile"; + MITK_INFO << "closing logfile (" << logFileName << ")" ; logFile->close(); delete logFile; logFile = 0; } if(file) { logFile = new std::ofstream( file, std::ios_base::out | std::ios_base::app ); + logFileName = file; /* if(*logFile) { std::cout << "opening logfile '" << file << "' for writing failed"; MITK_INFO << "logging to '" << file << "'"; } else { std::cerr << "opening logfile '" << file << "' for writing failed"; MITK_ERROR << "opening logfile '" << file << "' for writing failed"; delete logFile; logFile = 0; } */ } logMutex.Unlock(); } +std::string mitk::LoggingBackend::GetLogFile() +{ + return logFileName; +} + void mitk::LoggingBackend::CatchLogFileCommandLineParameter(int &argc,char **argv) { int r; for(r=1;r=argc) { --argc; MITK_ERROR << "--logfile parameter found, but no file given"; return; } mitk::LoggingBackend::SetLogFile(argv[r+1]); for(r+=2;r #include namespace mitk { /*! \brief mbilog backend implementation for mitk */ class MITK_CORE_EXPORT LoggingBackend : public mbilog::TextBackendBase { public: /** \brief overloaded method for receiving log message from mbilog */ void ProcessMessage(const mbilog::LogMessage& ); /** \brief registers MITK logging backend at mbilog */ static void Register(); /** \brief Unregisters MITK logging backend at mbilog */ static void Unregister(); /** \brief Sets extra log file path (additionally to the console log) */ static void SetLogFile(const char *file); + + /** @return Returns the log file if there is one. Returns an empty string + * if no log file was set. + */ + static std::string GetLogFile(); /** \brief Enables an additional logging output window by means of itk::outputwindow * This might be relevant for showing log output in applications with no default output console */ static void EnableAdditionalConsoleWindow(bool enable); /** \brief Automatically extracts and removes the "--logfile " parameters from the standard C main(argc,argv) parameter list and calls SetLogFile if needed */ static void CatchLogFileCommandLineParameter(int &argc,char **argv); }; } #endif diff --git a/Core/Code/Testing/files.cmake b/Core/Code/Testing/files.cmake index 059107ca32..7d6a3cde9c 100644 --- a/Core/Code/Testing/files.cmake +++ b/Core/Code/Testing/files.cmake @@ -1,114 +1,115 @@ # tests with no extra command line parameter set(MODULE_TESTS mitkAccessByItkTest.cpp mitkCoreObjectFactoryTest.cpp mitkMaterialTest.cpp mitkActionTest.cpp mitkEnumerationPropertyTest.cpp mitkEventTest.cpp mitkFocusManagerTest.cpp mitkGenericPropertyTest.cpp mitkGeometry3DTest.cpp mitkGeometryDataToSurfaceFilterTest.cpp mitkGlobalInteractionTest.cpp mitkImageDataItemTest.cpp #mitkImageMapper2DTest.cpp mitkImageGeneratorTest.cpp mitkBaseDataTest.cpp #mitkImageToItkTest.cpp mitkInstantiateAccessFunctionTest.cpp mitkInteractorTest.cpp mitkITKThreadingTest.cpp # mitkLevelWindowManagerTest.cpp mitkLevelWindowTest.cpp mitkMessageTest.cpp #mitkPipelineSmartPointerCorrectnessTest.cpp mitkPixelTypeTest.cpp mitkPlaneGeometryTest.cpp mitkPointSetFileIOTest.cpp mitkPointSetTest.cpp mitkPointSetWriterTest.cpp mitkPointSetReaderTest.cpp mitkPointSetInteractorTest.cpp mitkPropertyTest.cpp mitkPropertyListTest.cpp #mitkRegistrationBaseTest.cpp #mitkSegmentationInterpolationTest.cpp mitkSlicedGeometry3DTest.cpp mitkSliceNavigationControllerTest.cpp mitkStateMachineTest.cpp mitkStateTest.cpp mitkSurfaceTest.cpp mitkSurfaceToSurfaceFilterTest.cpp mitkTimeSlicedGeometryTest.cpp mitkTransitionTest.cpp mitkUndoControllerTest.cpp mitkVtkWidgetRenderingTest.cpp mitkVerboseLimitedLinearUndoTest.cpp mitkWeakPointerTest.cpp mitkTransferFunctionTest.cpp #mitkAbstractTransformGeometryTest.cpp mitkStepperTest.cpp itkTotalVariationDenoisingImageFilterTest.cpp mitkRenderingManagerTest.cpp vtkMitkThickSlicesFilterTest.cpp mitkNodePredicateSourceTest.cpp mitkVectorTest.cpp mitkClippedSurfaceBoundsCalculatorTest.cpp #QmitkRenderingTestHelper.cpp mitkExceptionTest.cpp mitkExtractSliceFilterTest.cpp + mitkLogTest.cpp ) # test with image filename as an extra command line parameter set(MODULE_IMAGE_TESTS mitkPlanePositionManagerTest.cpp mitkSurfaceVtkWriterTest.cpp #mitkImageSliceSelectorTest.cpp mitkImageTimeSelectorTest.cpp # mitkVtkPropRendererTest.cpp mitkDataNodeFactoryTest.cpp #mitkSTLFileReaderTest.cpp ) # list of images for which the tests are run set(MODULE_TESTIMAGES # Pic-Factory no more available in Core, test images now in .nrrd format US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd binary.stl ball.stl ) set(MODULE_CUSTOM_TESTS #mitkLabeledImageToSurfaceFilterTest.cpp #mitkExternalToolsTest.cpp mitkDataStorageTest.cpp mitkDataNodeTest.cpp mitkDicomSeriesReaderTest.cpp mitkDICOMLocaleTest.cpp mitkEventMapperTest.cpp mitkNodeDependentPointSetInteractorTest.cpp mitkStateMachineFactoryTest.cpp mitkPointSetLocaleTest.cpp mitkImageTest.cpp mitkImageWriterTest.cpp mitkImageVtkMapper2DTest.cpp mitkImageVtkMapper2DLevelWindowTest.cpp mitkImageVtkMapper2DOpacityTest.cpp mitkImageVtkMapper2DColorTest.cpp ) # Create an artificial module initializing class for # the usServiceListenerTest.cpp usFunctionGenerateModuleInit(testdriver_init_file NAME ${MODULE_NAME}TestDriver DEPENDS "Mitk" VERSION "0.1.0" EXECUTABLE ) set(TEST_CPP_FILES ${testdriver_init_file} mitkRenderingTestHelper.cpp) diff --git a/Core/Code/Testing/mitkLogTest.cpp b/Core/Code/Testing/mitkLogTest.cpp new file mode 100644 index 0000000000..db349addfc --- /dev/null +++ b/Core/Code/Testing/mitkLogTest.cpp @@ -0,0 +1,202 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkCommon.h" +#include "mitkTestingMacros.h" +#include +#include +#include +#include + + +/** Documentation + * + * @brief Objects of this class can start an internal thread by calling the Start() method. + * The thread is then logging messages until the method Stop() is called. The class + * can be used to test if logging is thread-save by using multiple objects and let + * them log simuntanously. + */ +class mitkTestLoggingThread +{ +protected: + +bool LoggingRunning; + +int ThreadID; + +itk::MultiThreader::Pointer m_MultiThreader; + +void LogMessages() + { + + while(LoggingRunning) + { + MITK_INFO << "Test info stream in thread " << ThreadID; + MITK_WARN << "Test warning stream in thread " << ThreadID; + MITK_DEBUG << "Test debugging stream in thread " << ThreadID; + MITK_ERROR << "Test error stream in thread " << ThreadID; + MITK_FATAL << "Test fatal stream in thread " << ThreadID; + } + } + + +static ITK_THREAD_RETURN_TYPE mitkTestLoggingThread::ThreadStartTracking(void* pInfoStruct) + { + /* extract this pointer from Thread Info structure */ + struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; + if (pInfo == NULL) + { + return ITK_THREAD_RETURN_VALUE; + } + if (pInfo->UserData == NULL) + { + return ITK_THREAD_RETURN_VALUE; + } + mitkTestLoggingThread *thisthread = (mitkTestLoggingThread*)pInfo->UserData; + + if (thisthread != NULL) + thisthread->LogMessages(); + + return ITK_THREAD_RETURN_VALUE; + } + +public: + +mitkTestLoggingThread(int number, itk::MultiThreader::Pointer MultiThreader) + { + ThreadID = number; + m_MultiThreader = MultiThreader; + } + +void Start() + { + LoggingRunning = true; + m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); + } + +void Stop() + { + LoggingRunning = false; + } + +}; + +/** Documentation + * + * @brief This class holds static test methods to sturcture the test of the mitk logging mechanism. + */ +class mitkLogTestClass +{ + +public: + +static void TestSimpleLog() + { + bool testSucceded = true; + try + { + MITK_INFO << "Test info stream."; + MITK_WARN << "Test warning stream."; + MITK_DEBUG << "Test debugging stream."; //only activated if cmake variable is on! + //so no worries if you see no output for this line + MITK_ERROR << "Test error stream."; + MITK_FATAL << "Test fatal stream."; + } + catch(mitk::Exception e) + { + testSucceded = false; + } + MITK_TEST_CONDITION_REQUIRED(testSucceded,"Test logging streams."); + } + +static void TestThreadSaveLog() + { + bool testSucceded = true; + + try + { + //initialize two threads... + itk::MultiThreader::Pointer myThreader = itk::MultiThreader::New(); + mitkTestLoggingThread myThreadClass1 = mitkTestLoggingThread(1,myThreader); + mitkTestLoggingThread myThreadClass2 = mitkTestLoggingThread(2,myThreader); + + //start them + myThreadClass1.Start(); + myThreadClass2.Start(); + + + //wait for 500 ms + itksys::SystemTools::Delay(500); + + //stop them + myThreadClass1.Stop(); + myThreadClass2.Stop(); + + //sleep again to let all threads end + itksys::SystemTools::Delay(200); + } + catch(...) + { + testSucceded = false; + } + + //if no error occured until now, everything is ok + MITK_TEST_CONDITION_REQUIRED(testSucceded,"Test logging in different threads."); + } + +static void TestLoggingToFile() + { + std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + "/testlog.log"; + mitk::LoggingBackend::SetLogFile(filename.c_str()); + MITK_INFO << "Test logging to default filename: " << mitk::LoggingBackend::GetLogFile(); + MITK_TEST_CONDITION_REQUIRED(itksys::SystemTools::FileExists(filename.c_str()),"Testing if log file exists."); + //TODO delete log file? + } + +static void TestAddAndRemoveBackends() + { + mbilog::BackendCout myBackend = mbilog::BackendCout(); + mbilog::RegisterBackend(&myBackend); + MITK_INFO << "Test logging"; + mbilog::UnregisterBackend(&myBackend); + + //if no error occured until now, everything is ok + MITK_TEST_CONDITION_REQUIRED(true,"Test add/remove logging backend."); + } + +static void TestDefaultBackend() + { + //not possible now, because we cannot unregister the mitk logging backend in the moment. If such a method is added to mbilog utility one may add this test. + } + + +}; + +int mitkLogTest(int /* argc */, char* /*argv*/[]) +{ + // always start with this! + MITK_TEST_BEGIN("Log") + + MITK_TEST_OUTPUT(<<"TESTING ALL LOGGING OUTPUTS, ERROR MESSAGES ARE ALSO TESTED AND NOT MEANING AN ERROR OCCURED!") + + mitkLogTestClass::TestSimpleLog(); + mitkLogTestClass::TestThreadSaveLog(); + mitkLogTestClass::TestLoggingToFile(); + mitkLogTestClass::TestAddAndRemoveBackends(); + + // always end with this! + MITK_TEST_END() +} diff --git a/Plugins/org.mitk.core.services/src/internal/mitkPluginActivator.cpp b/Plugins/org.mitk.core.services/src/internal/mitkPluginActivator.cpp index 91dc708941..b6d4b8fe8a 100644 --- a/Plugins/org.mitk.core.services/src/internal/mitkPluginActivator.cpp +++ b/Plugins/org.mitk.core.services/src/internal/mitkPluginActivator.cpp @@ -1,258 +1,274 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPluginActivator.h" +#include "mitkLog.h" + +#include +#include + #include "internal/mitkDataStorageService.h" #include #include #include namespace mitk { class ITKLightObjectToQObjectAdapter : public QObject { public: ITKLightObjectToQObjectAdapter(const QStringList& clazzes, itk::LightObject* service) : interfaceNames(clazzes), mitkService(service) {} // This method is called by the Qt meta object system. It is usually // generated by the moc, but we create it manually to be able to return // a MITK micro service object (derived from itk::LightObject). It basically // works as if the micro service class had used the Q_INTERFACES macro in // its declaration. Now we can successfully do a // qobject_cast(lightObjectToQObjectAdapter) void* qt_metacast(const char *_clname) { if (!_clname) return 0; if (!strcmp(_clname, "ITKLightObjectToQObjectAdapter")) return static_cast(const_cast(this)); if (interfaceNames.contains(QString(_clname))) return static_cast(mitkService); return QObject::qt_metacast(_clname); } private: QStringList interfaceNames; itk::LightObject* mitkService; }; const std::string org_mitk_core_services_Activator::PLUGIN_ID = "org.mitk.core.services"; void org_mitk_core_services_Activator::start(ctkPluginContext* context) { pluginContext = context; + //initialize logging + mitk::LoggingBackend::Register(); + QString filename = "mitk.log"; + QFileInfo path = context->getDataFile(filename); + mitk::LoggingBackend::SetLogFile(path.absoluteFilePath().toStdString().c_str()); + MITK_INFO << "Logfile: " << path.absoluteFilePath().toStdString().c_str(); + + //initialize data storage service DataStorageService* service = new DataStorageService(); dataStorageService = IDataStorageService::Pointer(service); context->registerService(service); // Get the MitkCore Module Context mitkContext = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Process all already registered services std::list refs = mitkContext->GetServiceReferences(""); for (std::list::const_iterator i = refs.begin(); i != refs.end(); ++i) { this->AddMitkService(*i); } mitkContext->AddServiceListener(this, &org_mitk_core_services_Activator::MitkServiceChanged); } void org_mitk_core_services_Activator::stop(ctkPluginContext* /*context*/) { mitkContext->RemoveServiceListener(this, &org_mitk_core_services_Activator::MitkServiceChanged); foreach(ctkServiceRegistration reg, mapMitkIdToRegistration.values()) { reg.unregister(); } mapMitkIdToRegistration.clear(); qDeleteAll(mapMitkIdToAdapter); mapMitkIdToAdapter.clear(); + //clean up logging + mitk::LoggingBackend::Unregister(); + dataStorageService = 0; mitkContext = 0; pluginContext = 0; } void org_mitk_core_services_Activator::MitkServiceChanged(const mitk::ServiceEvent event) { switch (event.GetType()) { case mitk::ServiceEvent::REGISTERED: { this->AddMitkService(event.GetServiceReference()); break; } case mitk::ServiceEvent::UNREGISTERING: { long mitkServiceId = mitk::any_cast(event.GetServiceReference().GetProperty(mitk::ServiceConstants::SERVICE_ID())); ctkServiceRegistration reg = mapMitkIdToRegistration.take(mitkServiceId); if (reg) { reg.unregister(); } delete mapMitkIdToAdapter.take(mitkServiceId); break; } case mitk::ServiceEvent::MODIFIED: { long mitkServiceId = mitk::any_cast(event.GetServiceReference().GetProperty(mitk::ServiceConstants::SERVICE_ID())); ctkDictionary newProps = CreateServiceProperties(event.GetServiceReference()); mapMitkIdToRegistration[mitkServiceId].setProperties(newProps); break; } default: break; // do nothing } } void org_mitk_core_services_Activator::AddMitkService(const mitk::ServiceReference& ref) { // Get the MITK micro service object itk::LightObject* mitkService = mitkContext->GetService(ref); if (mitkService == 0) return; // Get the interface names against which the service was registered std::list clazzes = mitk::any_cast >(ref.GetProperty(mitk::ServiceConstants::OBJECTCLASS())); QStringList qclazzes; for(std::list::const_iterator clazz = clazzes.begin(); clazz != clazzes.end(); ++clazz) { qclazzes << QString::fromStdString(*clazz); } long mitkServiceId = mitk::any_cast(ref.GetProperty(mitk::ServiceConstants::SERVICE_ID())); QObject* adapter = new ITKLightObjectToQObjectAdapter(qclazzes, mitkService); mapMitkIdToAdapter[mitkServiceId] = adapter; ctkDictionary props = CreateServiceProperties(ref); mapMitkIdToRegistration[mitkServiceId] = pluginContext->registerService(qclazzes, adapter, props); } ctkDictionary org_mitk_core_services_Activator::CreateServiceProperties(const ServiceReference &ref) { ctkDictionary props; long mitkServiceId = mitk::any_cast(ref.GetProperty(mitk::ServiceConstants::SERVICE_ID())); props.insert("mitk.serviceid", QVariant::fromValue(mitkServiceId)); // Add all other properties from the MITK micro service std::vector keys; ref.GetPropertyKeys(keys); for (std::vector::const_iterator it = keys.begin(); it != keys.end(); ++it) { QString key = QString::fromStdString(*it); mitk::Any value = ref.GetProperty(*it); // We cannot add any mitk::Any object, we need to query the type const std::type_info& objType = value.Type(); if (objType == typeid(std::string)) { props.insert(key, QString::fromStdString(ref_any_cast(value))); } else if (objType == typeid(std::vector)) { const std::vector& list = ref_any_cast >(value); QStringList qlist; for (std::vector::const_iterator str = list.begin(); str != list.end(); ++str) { qlist << QString::fromStdString(*str); } props.insert(key, qlist); } else if (objType == typeid(std::list)) { const std::list& list = ref_any_cast >(value); QStringList qlist; for (std::list::const_iterator str = list.begin(); str != list.end(); ++str) { qlist << QString::fromStdString(*str); } props.insert(key, qlist); } else if (objType == typeid(char)) { props.insert(key, QChar(ref_any_cast(value))); } else if (objType == typeid(unsigned char)) { props.insert(key, QChar(ref_any_cast(value))); } else if (objType == typeid(bool)) { props.insert(key, any_cast(value)); } else if (objType == typeid(short)) { props.insert(key, any_cast(value)); } else if (objType == typeid(unsigned short)) { props.insert(key, any_cast(value)); } else if (objType == typeid(int)) { props.insert(key, any_cast(value)); } else if (objType == typeid(unsigned int)) { props.insert(key, any_cast(value)); } else if (objType == typeid(float)) { props.insert(key, any_cast(value)); } else if (objType == typeid(double)) { props.insert(key, any_cast(value)); } else if (objType == typeid(long long int)) { props.insert(key, any_cast(value)); } else if (objType == typeid(unsigned long long int)) { props.insert(key, any_cast(value)); } } return props; } org_mitk_core_services_Activator::org_mitk_core_services_Activator() : mitkContext(0), pluginContext(0) { } } Q_EXPORT_PLUGIN2(org_mitk_core_services, mitk::org_mitk_core_services_Activator) diff --git a/Utilities/mbilog/mbilog.cpp b/Utilities/mbilog/mbilog.cpp index 9ff91b13ca..c1d3e760a2 100644 --- a/Utilities/mbilog/mbilog.cpp +++ b/Utilities/mbilog/mbilog.cpp @@ -1,68 +1,68 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include "mbilog.h" static std::list backends; namespace mbilog { static const std::string NA_STRING = "n/a"; } void mbilog::RegisterBackend(mbilog::BackendBase* backend) { backends.push_back(backend); } void mbilog::UnregisterBackend(mbilog::BackendBase* backend) { backends.remove(backend); } void mbilog::DistributeToBackends(mbilog::LogMessage &l) { //Crop Message { std::string::size_type i = l.message.find_last_not_of(" \t\f\v\n\r"); l.message = (i != std::string::npos) ? l.message.substr(0, i+1) : ""; } //create dummy backend if there is no backend registered (so we have an output anyway) - mbilog::BackendCout* dummyBackend = NULL; - if(backends.empty()) - { - dummyBackend = new mbilog::BackendCout(); + static mbilog::BackendCout* dummyBackend; + if(backends.empty() && (dummyBackend == NULL)) + { + dummyBackend = new mbilog::BackendCout(); dummyBackend->SetFull(false); RegisterBackend(dummyBackend); - } + } + else if((backends.size()>1) && (dummyBackend != NULL)) + { + //if there was added another backend remove the dummy backend and delete it + UnregisterBackend(dummyBackend); + delete dummyBackend; + dummyBackend = NULL; + } //iterate through all registered images and call the ProcessMessage() methods of the backends std::list::iterator i; for(i = backends.begin(); i != backends.end(); i++) (*i)->ProcessMessage(l); - - //if there was added a dummy backend remove it now - if (dummyBackend != NULL) - { - UnregisterBackend(dummyBackend); - delete dummyBackend; - } }