Kaynağa Gözat

Add support for error log context

Jean-Christophe Fillion-Robin 10 yıl önce
ebeveyn
işleme
44c505e0bc

+ 1 - 0
Libs/Core/CMakeLists.txt

@@ -41,6 +41,7 @@ set(KIT_SRCS
   ctkDependencyGraph.h
   ctkErrorLogAbstractMessageHandler.cpp
   ctkErrorLogAbstractMessageHandler.h
+  ctkErrorLogContext.h
   ctkErrorLogFDMessageHandler.cpp
   ctkErrorLogFDMessageHandler.h
   ctkErrorLogFDMessageHandler_p.h

+ 37 - 7
Libs/Core/Testing/Cpp/ctkErrorLogModelFileLoggingTest1.cpp

@@ -42,7 +42,6 @@ int ctkErrorLogModelFileLoggingTest1(int argc, char * argv [])
   QCoreApplication app(argc, argv);
   Q_UNUSED(app);
   ctkErrorLogModel model;
-  //model.setAsynchronousLogging(false);
 
   // fileLoggingEnabled
   if (!checkBoolean(__LINE__, "FileLoggingEnabled", model.fileLoggingEnabled(), false).isEmpty())
@@ -114,9 +113,11 @@ int ctkErrorLogModelFileLoggingTest1(int argc, char * argv [])
   fdMessage0.append(fdMessage0b);
   fflush(stdout);
 
-  QString fdMessage1("This is a 2nd stdout message");
-  fprintf(stdout, "%s\n", qPrintable(fdMessage1));
-  fflush(stdout);
+  // XXX FD messages from stderr and stdout are not always reported in the same order
+
+  //QString fdMessage1("This is a 2nd stdout message");
+  //fprintf(stdout, "%s\n", qPrintable(fdMessage1));
+  //fflush(stdout);
 
   QString fdMessage2("This is a stderr");
   fprintf(stderr, "%s", qPrintable(fdMessage2));
@@ -125,12 +126,41 @@ int ctkErrorLogModelFileLoggingTest1(int argc, char * argv [])
   fdMessage2.append(fdMessage2b);
   fflush(stderr);
 
-  QString fdMessage3("This is a 2nd stderr message");
-  fprintf(stderr, "%s\n", qPrintable(fdMessage3));
-  fflush(stderr);
+  //QString fdMessage3("This is a 2nd stderr message");
+  //fprintf(stderr, "%s\n", qPrintable(fdMessage3));
+  //fflush(stderr);
 
   // Give enough time to the ErrorLogModel to consider the queued messages.
   processEvents(1000);
 
+  model.disableAllMsgHandler();
+
+  QList<QStringList> expectedLogEntries;
+  expectedLogEntries << (QStringList() << "DEBUG" << "Qt" << qtMessage0);
+  expectedLogEntries << (QStringList() << "WARNING" << "Qt" << qtMessage1);
+  expectedLogEntries << (QStringList() << "CRITICAL" << "Qt" << qtMessage2);
+  expectedLogEntries << (QStringList() << "INFO" << "Stream" << streamMessage0);
+  expectedLogEntries << (QStringList() << "CRITICAL" << "Stream" << streamMessage1);
+  expectedLogEntries << (QStringList() << "INFO" << "FD" << fdMessage0);
+  //expectedLogEntries << (QStringList() << "INFO" << "FD" << fdMessage1);
+  expectedLogEntries << (QStringList() << "CRITICAL" << "FD" << fdMessage2);
+  //expectedLogEntries << (QStringList() << "CRITICAL" << "FD" << fdMessage3);
+
+  QStringList currentLogEntries = readFile(logFilePath);
+
+  QString expectedLogEntryPatternTemplate("^\\[%1\\]\\[%2\\] [0-9\\.\\s\\:]+ \\[\\] \\(unknown\\:0\\) \\- %3$");
+  for(int entryIndex = 0; entryIndex < expectedLogEntries.size(); ++entryIndex)
+    {
+    QStringList entry = expectedLogEntries.at(entryIndex);
+    QRegExp regexp(expectedLogEntryPatternTemplate.arg(entry.at(0)).arg(entry.at(1)).arg(entry.at(2)));
+    if (!regexp.exactMatch(currentLogEntries.at(entryIndex)))
+      {
+      printErrorMessage(
+            QString("Line %1 - Log entry %2 does NOT math expected regular expression.\n\tLogEntry: %3\n\tRegExp: %4").
+                arg(__LINE__).arg(entryIndex).arg(currentLogEntries.at(entryIndex)).arg(regexp.pattern()));
+      return EXIT_FAILURE;
+      }
+    }
+
   return EXIT_SUCCESS;
 }

+ 8 - 2
Libs/Core/ctkErrorLogAbstractMessageHandler.cpp

@@ -20,9 +20,13 @@
 
 #include "ctkErrorLogAbstractMessageHandler.h"
 
+// Qt includes
 #include <QHash>
 #include <QDateTime>
 
+// CTK includes
+#include "ctkErrorLogContext.h"
+
 // --------------------------------------------------------------------------
 // ctkErrorLogAbstractMessageHandlerPrivate
 
@@ -110,7 +114,9 @@ void ctkErrorLogAbstractMessageHandler::setEnabled(bool value)
 // --------------------------------------------------------------------------
 void ctkErrorLogAbstractMessageHandler::handleMessage(const QString& threadId,
                                                       ctkErrorLogLevel::LogLevel logLevel,
-                                                      const QString& origin, const QString& text)
+                                                      const QString& origin,
+                                                      const ctkErrorLogContext& logContext,
+                                                      const QString &text)
 {
   Q_D(ctkErrorLogAbstractMessageHandler);
   if (logLevel <= ctkErrorLogLevel::Info)
@@ -127,7 +133,7 @@ void ctkErrorLogAbstractMessageHandler::handleMessage(const QString& threadId,
       d->TerminalOutputs.value(ctkErrorLogTerminalOutput::StandardError)->output(text);
       }
     }
-  emit this->messageHandled(QDateTime::currentDateTime(), threadId, logLevel, origin, text);
+  emit this->messageHandled(QDateTime::currentDateTime(), threadId, logLevel, origin, logContext, text);
 }
 
 // --------------------------------------------------------------------------

+ 4 - 2
Libs/Core/ctkErrorLogAbstractMessageHandler.h

@@ -32,6 +32,7 @@
 
 //------------------------------------------------------------------------------
 class ctkErrorLogAbstractMessageHandlerPrivate;
+struct ctkErrorLogContext;
 
 //------------------------------------------------------------------------------
 /// \ingroup Core
@@ -52,7 +53,8 @@ public:
   void setEnabled(bool value);
 
   void handleMessage(const QString& threadId, ctkErrorLogLevel::LogLevel logLevel,
-                     const QString& origin, const QString& text);
+                     const QString& origin, const ctkErrorLogContext& logContext,
+                     const QString &text);
 
   ctkErrorLogTerminalOutput* terminalOutput(ctkErrorLogTerminalOutput::TerminalOutput terminalOutputType)const;
   void setTerminalOutput(ctkErrorLogTerminalOutput::TerminalOutput terminalOutputType,
@@ -61,7 +63,7 @@ public:
 Q_SIGNALS:
   void messageHandled(const QDateTime& currentDateTime, const QString& threadId,
                       ctkErrorLogLevel::LogLevel logLevel, const QString& origin,
-                      const QString& text);
+                      const ctkErrorLogContext& logContext, const QString& text);
 
 protected:
   void setHandlerPrettyName(const QString& newHandlerPrettyName);

+ 44 - 0
Libs/Core/ctkErrorLogContext.h

@@ -0,0 +1,44 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkErrorLogContext_h
+#define __ctkErrorLogContext_h
+
+// Qt includes
+#include <QString>
+
+// CTK includes
+#include "ctkCoreExport.h"
+
+//------------------------------------------------------------------------------
+/// \ingroup Core
+struct CTK_CORE_EXPORT ctkErrorLogContext
+{
+  ctkErrorLogContext():Line(0),File("unknown"), Function("unknown"), Message(""){}
+  ctkErrorLogContext(const QString& msg):
+    Line(0),File("unknown"), Function("unknown"), Message(msg){}
+  QString Category;
+  int Line;
+  QString File;
+  QString Function;
+  QString Message;
+};
+
+#endif

+ 2 - 0
Libs/Core/ctkErrorLogFDMessageHandler.cpp

@@ -23,6 +23,7 @@
 #include <QFile>
 
 // CTK includes
+#include "ctkErrorLogContext.h"
 #include "ctkErrorLogFDMessageHandler.h"
 #include "ctkErrorLogFDMessageHandler_p.h"
 #include "ctkUtils.h"
@@ -208,6 +209,7 @@ void ctkFDHandler::run()
       ctk::qtHandleToString(QThread::currentThreadId()),
       this->LogLevel,
       this->MessageHandler->handlerPrettyName(),
+      ctkErrorLogContext(line),
       line);
     }
 }

+ 2 - 1
Libs/Core/ctkErrorLogQtMessageHandler.cpp

@@ -24,6 +24,7 @@
 #include <QVariant>
 
 // CTK includes
+#include "ctkErrorLogContext.h"
 #include "ctkErrorLogQtMessageHandler.h"
 #include <ctkUtils.h>
 
@@ -138,7 +139,7 @@ void ctkErrorLogModelQtMessageOutput(QtMsgType type, const char *msg)
 //    //  }
     handler->handleMessage(
           ctk::qtHandleToString(QThread::currentThreadId()),
-          level, handler->handlerPrettyName(), msg);
+          level, handler->handlerPrettyName(), ctkErrorLogContext(msg), msg);
     }
 }
 #endif

+ 6 - 2
Libs/Core/ctkErrorLogStreamMessageHandler.cpp

@@ -24,6 +24,7 @@
 #include <QHash>
 
 // CTK includes
+#include "ctkErrorLogContext.h"
 #include "ctkErrorLogStreamMessageHandler.h"
 #include "ctkUtils.h"
 
@@ -62,6 +63,7 @@ private:
 
   ctkErrorLogStreamMessageHandler * MessageHandler;
   ctkErrorLogLevel::LogLevel LogLevel;
+  ctkErrorLogContext LogContext;
 
   bool Enabled;
 
@@ -137,7 +139,8 @@ std::streambuf::int_type ctkStreamHandler::overflow(std::streambuf::int_type v)
     Q_ASSERT(this->MessageHandler);
     this->MessageHandler->handleMessage(
           ctk::qtHandleToString(QThread::currentThreadId()), this->LogLevel,
-          this->MessageHandler->handlerPrettyName(), this->currentBuffer()->c_str());
+          this->MessageHandler->handlerPrettyName(),
+          ctkErrorLogContext(this->currentBuffer()->c_str()), this->currentBuffer()->c_str());
     this->currentBuffer()->erase(this->currentBuffer()->begin(), this->currentBuffer()->end());
     }
   else
@@ -164,7 +167,8 @@ std::streamsize ctkStreamHandler::xsputn(const char *p, std::streamsize n)
       Q_ASSERT(this->MessageHandler);
       this->MessageHandler->handleMessage(
             ctk::qtHandleToString(QThread::currentThreadId()), this->LogLevel,
-            this->MessageHandler->handlerPrettyName(), tmp.c_str());
+            this->MessageHandler->handlerPrettyName(),
+            ctkErrorLogContext(tmp.c_str()), tmp.c_str());
       this->currentBuffer()->erase(this->currentBuffer()->begin(), this->currentBuffer()->begin() + pos + 1);
       }
     }

+ 2 - 0
Libs/ImageProcessing/ITK/Core/Testing/Cpp/CMakeLists.txt

@@ -5,6 +5,7 @@ set(KIT ${PROJECT_NAME})
 #
 set(TEST_SOURCES
   ctkITKErrorLogMessageHandlerWithThreadsTest1.cpp
+  ctkITKErrorLogModelFileLoggingTest1.cpp
   ctkITKErrorLogModelTest1.cpp
   )
 
@@ -62,6 +63,7 @@ target_link_libraries(${KIT}CppTests ${LIBRARY_NAME} ${CTK_BASE_LIBRARIES} CTKWi
 #
 
 SIMPLE_TEST( ctkITKErrorLogMessageHandlerWithThreadsTest1 )
+SIMPLE_TEST( ctkITKErrorLogModelFileLoggingTest1 )
 SIMPLE_TEST( ctkITKErrorLogModelTest1 )
 
 #

+ 128 - 0
Libs/ImageProcessing/ITK/Core/Testing/Cpp/ctkITKErrorLogModelFileLoggingTest1.cpp

@@ -0,0 +1,128 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QCoreApplication>
+#include <QDebug>
+#include <QDir>
+#include <QTemporaryFile>
+
+// CTK includes
+#include "ctkITKErrorLogMessageHandler.h"
+
+// VTK includes
+#include <itkObject.h>
+#include <itkObjectFactory.h>
+#include <itkOutputWindow.h>
+
+// STL includes
+#include <cstdlib>
+
+// Helper functions
+#include "Testing/Cpp/ctkErrorLogModelTestHelper.cpp"
+
+// --------------------------------------------------------------------------
+namespace itk
+{
+
+class ctkITKFileLoggingTestObject : public Object
+{
+public:
+  /** Standard class typedefs. */
+  typedef ctkITKFileLoggingTestObject   Self;
+  typedef Object                        Superclass;
+  typedef SmartPointer<Self>            Pointer;
+  typedef SmartPointer<const Self>      ConstPointer;
+
+  /** Standard New method. */
+  itkNewMacro(ctkITKFileLoggingTestObject);
+
+  /** Run-time type information (and related methods). */
+  itkTypeMacro(ctkITKFileLoggingTestObject, Object);
+
+  ctkITKFileLoggingTestObject(){}
+  ~ctkITKFileLoggingTestObject(){}
+
+  void exerciseITKDebugMacro(const QString& text)
+  {
+    itkDebugMacro( << qPrintable(text));
+  }
+
+  void exerciseITKWarningMacro(const QString& text)
+  {
+    itkWarningMacro( << qPrintable(text));
+  }
+};
+
+} // End of itk namespace
+
+//-----------------------------------------------------------------------------
+int ctkITKErrorLogModelFileLoggingTest1(int argc, char * argv [])
+{
+  QCoreApplication app(argc, argv);
+  Q_UNUSED(app);
+  ctkErrorLogModel model;
+  model.setFileLoggingEnabled(true);
+
+  // Create log file
+  QTemporaryFile logFile(QDir::tempPath() + "/ctkITKErrorLogModelFileLoggingTest1.XXXXXX");
+  logFile.setAutoRemove(false);
+  logFile.open();
+  logFile.close();
+  QString logFilePath = logFile.fileName();
+
+  model.setFilePath(logFilePath);
+
+  // Monitor VTK messages
+  model.registerMsgHandler(new ctkITKErrorLogMessageHandler);
+  model.setMsgHandlerEnabled(ctkITKErrorLogMessageHandler::HandlerName, true);
+
+  itk::ctkITKFileLoggingTestObject::Pointer object = itk::ctkITKFileLoggingTestObject::New();
+
+  QString itkMessage0("This is a ITK debug message");
+  object->exerciseITKDebugMacro(itkMessage0);
+
+  QString itkMessage1("This is a ITK warning message");
+  object->exerciseITKWarningMacro(itkMessage1);
+
+
+  // Give enough time to the ErrorLogModel to consider the queued messages.
+  processEvents(1000);
+
+  model.disableAllMsgHandler();
+
+  QStringList logLines = readFile(logFilePath);
+
+  QString expectedLogEntryPatternTemplate(
+        "^\\[%1\\]\\[ITK\\] [0-9\\.\\s\\:]+ \\[ctkITKFileLoggingTestObject \\(0x[a-zA-B0-9]+\\)\\] "
+        "\\(.+ctkITKErrorLogModelFileLoggingTest1\\.cpp\\:%2\\) \\- %3$");
+
+  int entryIndex = 0;
+  QRegExp regexp(expectedLogEntryPatternTemplate.arg("WARNING").arg(70).arg("This is a ITK warning message"));
+  if (!regexp.exactMatch(logLines.at(entryIndex)))
+    {
+    printErrorMessage(
+          QString("Line %1 - Log entry %2 does NOT math expected regular expression.\n\tLogEntry: %3\n\tRegExp: %4").
+              arg(__LINE__).arg(entryIndex).arg(logLines.at(entryIndex)).arg(regexp.pattern()));
+    return EXIT_FAILURE;
+    }
+
+  return EXIT_SUCCESS;
+}

+ 37 - 6
Libs/ImageProcessing/ITK/Core/ctkITKErrorLogMessageHandler.cpp

@@ -22,6 +22,7 @@
 #include <QThread>
 
 // CTK includes
+#include "ctkErrorLogContext.h"
 #include "ctkITKErrorLogMessageHandler.h"
 #include "ctkUtils.h"
 
@@ -58,7 +59,8 @@ public:
   /** Run-time type information (and related methods). */
   itkTypeMacro(ctkITKOutputWindow, OutputWindow);
 
-  ctkITKOutputWindow():MessageHandler(0){}
+  ctkITKOutputWindow():MessageHandler(0),
+    ContextRegExp("[a-zA-Z\\s]+: In (.+), line ([\\d]+)\\n(.+\\(0x[a-fA-F0-9]+\\))\\:\\s(.*)"){}
   ~ctkITKOutputWindow(){}
 
   virtual void DisplayText(const char*);
@@ -68,7 +70,11 @@ public:
 
   virtual void DisplayDebugText(const char*);
 
+  QString parseText(const QString &text, ctkErrorLogContext &context);
+
   ctkErrorLogAbstractMessageHandler * MessageHandler;
+
+  QRegExp ContextRegExp;
 };
 
 // --------------------------------------------------------------------------
@@ -81,27 +87,35 @@ void ctkITKOutputWindow::DisplayText(const char* text)
   this->MessageHandler->handleMessage(
         ctk::qtHandleToString(QThread::currentThreadId()),
         ctkErrorLogLevel::Info,
-        this->MessageHandler->handlerPrettyName(), text);
+        this->MessageHandler->handlerPrettyName(), ctkErrorLogContext(), text);
 }
 
 //----------------------------------------------------------------------------
 void ctkITKOutputWindow::DisplayErrorText(const char* text)
 {
   Q_ASSERT(this->MessageHandler);
+
+  ctkErrorLogContext context;
+  this->parseText(text, context);
+
   this->MessageHandler->handleMessage(
         ctk::qtHandleToString(QThread::currentThreadId()),
         ctkErrorLogLevel::Error,
-        this->MessageHandler->handlerPrettyName(), text);
+        this->MessageHandler->handlerPrettyName(), context, text);
 }
 
 //----------------------------------------------------------------------------
 void ctkITKOutputWindow::DisplayWarningText(const char* text)
 {
   Q_ASSERT(this->MessageHandler);
+
+  ctkErrorLogContext context;
+  this->parseText(text, context);
+
   this->MessageHandler->handleMessage(
         ctk::qtHandleToString(QThread::currentThreadId()),
         ctkErrorLogLevel::Warning,
-        this->MessageHandler->handlerPrettyName(), text);
+        this->MessageHandler->handlerPrettyName(), context, text);
 }
 
 //----------------------------------------------------------------------------
@@ -114,10 +128,28 @@ void ctkITKOutputWindow::DisplayGenericWarningText(const char* text)
 void ctkITKOutputWindow::DisplayDebugText(const char* text)
 {
   Q_ASSERT(this->MessageHandler);
+
+  ctkErrorLogContext context;
+  this->parseText(text, context);
+
   this->MessageHandler->handleMessage(
         ctk::qtHandleToString(QThread::currentThreadId()),
         ctkErrorLogLevel::Debug,
-        this->MessageHandler->handlerPrettyName(), text);
+        this->MessageHandler->handlerPrettyName(), context, text);
+}
+
+//----------------------------------------------------------------------------
+QString ctkITKOutputWindow::parseText(const QString& text, ctkErrorLogContext& context)
+{
+  context.Message = text;
+  if (this->ContextRegExp.exactMatch(text))
+    {
+    context.File = this->ContextRegExp.cap(1);
+    context.Category = this->ContextRegExp.cap(3);
+    context.Line = this->ContextRegExp.cap(2).toInt();
+    context.Message = this->ContextRegExp.cap(4);
+    }
+  return context.Message;
 }
 
 } // End of itk namespace
@@ -199,4 +231,3 @@ void ctkITKErrorLogMessageHandler::setEnabledInternal(bool value)
     d->SavedITKOutputWindow = 0;
     }
 }
-

+ 37 - 6
Libs/Visualization/VTK/Core/ctkVTKErrorLogMessageHandler.cpp

@@ -22,6 +22,7 @@
 #include <QThread>
 
 // CTK includes
+#include "ctkErrorLogContext.h"
 #include "ctkVTKErrorLogMessageHandler.h"
 #include "ctkUtils.h"
 
@@ -42,7 +43,8 @@ public:
   vtkTypeMacro(ctkVTKOutputWindow,vtkOutputWindow);
   void PrintSelf(ostream& os, vtkIndent indent);
 
-  ctkVTKOutputWindow():MessageHandler(0){}
+  ctkVTKOutputWindow():MessageHandler(0),
+    ContextRegExp("[a-zA-Z\\s]+: In (.+), line ([\\d]+)\\n(.+\\(0x[a-fA-F0-9]+\\))\\:\\s(.*)"){}
   ~ctkVTKOutputWindow(){}
 
   virtual void DisplayText(const char*);
@@ -52,7 +54,11 @@ public:
 
   virtual void DisplayDebugText(const char*);
 
+  QString parseText(const QString &text, ctkErrorLogContext &context);
+
   ctkErrorLogAbstractMessageHandler * MessageHandler;
+
+  QRegExp ContextRegExp;
 };
 
 // --------------------------------------------------------------------------
@@ -74,27 +80,35 @@ void ctkVTKOutputWindow::DisplayText(const char* text)
   this->MessageHandler->handleMessage(
         ctk::qtHandleToString(QThread::currentThreadId()),
         ctkErrorLogLevel::Info,
-        this->MessageHandler->handlerPrettyName(), text);
+        this->MessageHandler->handlerPrettyName(), ctkErrorLogContext(), text);
 }
 
 //----------------------------------------------------------------------------
 void ctkVTKOutputWindow::DisplayErrorText(const char* text)
 {
   Q_ASSERT(this->MessageHandler);
+
+  ctkErrorLogContext context;
+  QString textOnly = this->parseText(text, context);
+
   this->MessageHandler->handleMessage(
         ctk::qtHandleToString(QThread::currentThreadId()),
         ctkErrorLogLevel::Error,
-        this->MessageHandler->handlerPrettyName(), text);
+        this->MessageHandler->handlerPrettyName(), context, textOnly);
 }
 
 //----------------------------------------------------------------------------
 void ctkVTKOutputWindow::DisplayWarningText(const char* text)
 {
   Q_ASSERT(this->MessageHandler);
+
+  ctkErrorLogContext context;
+  this->parseText(text, context);
+
   this->MessageHandler->handleMessage(
         ctk::qtHandleToString(QThread::currentThreadId()),
         ctkErrorLogLevel::Warning,
-        this->MessageHandler->handlerPrettyName(), text);
+        this->MessageHandler->handlerPrettyName(), context, text);
 }
 
 //----------------------------------------------------------------------------
@@ -107,10 +121,28 @@ void ctkVTKOutputWindow::DisplayGenericWarningText(const char* text)
 void ctkVTKOutputWindow::DisplayDebugText(const char* text)
 {
   Q_ASSERT(this->MessageHandler);
+
+  ctkErrorLogContext context;
+  this->parseText(text, context);
+
   this->MessageHandler->handleMessage(
         ctk::qtHandleToString(QThread::currentThreadId()),
         ctkErrorLogLevel::Debug,
-        this->MessageHandler->handlerPrettyName(), text);
+        this->MessageHandler->handlerPrettyName(), context, text);
+}
+
+//----------------------------------------------------------------------------
+QString ctkVTKOutputWindow::parseText(const QString& text, ctkErrorLogContext& context)
+{
+  context.Message = text;
+  if (this->ContextRegExp.exactMatch(text))
+    {
+    context.File = this->ContextRegExp.cap(1);
+    context.Category = this->ContextRegExp.cap(3);
+    context.Line = this->ContextRegExp.cap(2).toInt();
+    context.Message = this->ContextRegExp.cap(4);
+    }
+  return context.Message;
 }
 
 } // End of anonymous namespace
@@ -197,4 +229,3 @@ void ctkVTKErrorLogMessageHandler::setEnabledInternal(bool value)
     d->SavedVTKOutputWindow = 0;
     }
 }
-

+ 31 - 5
Libs/Visualization/VTK/Widgets/Testing/Cpp/ctkVTKErrorLogModelFileLoggingTest1.cpp

@@ -62,17 +62,43 @@ int ctkVTKErrorLogModelFileLoggingTest1(int argc, char * argv [])
   vtkNew<vtkObject> object;
 
   // VTK messages
-  //QString vtkMessage0("This is a VTK debug message");
   vtkDebugWithObjectMacro(object.GetPointer(), "This is a VTK debug message");
-
-  //QString vtkMessage1("This is a VTK warning message");
   vtkWarningWithObjectMacro(object.GetPointer(), "This is a VTK warning message");
-
-  //QString vtkMessage2("This is a VTK error message");
   vtkErrorWithObjectMacro(object.GetPointer(), "This is a VTK error message");
 
   // Give enough time to the ErrorLogModel to consider the queued messages.
   processEvents(1000);
 
+  model.disableAllMsgHandler();
+
+  QStringList logLines = readFile(logFilePath);
+
+  QString expectedLogEntryPatternTemplate(
+        "^\\[%1\\]\\[VTK\\] [0-9\\.\\s\\:]+ \\[vtkObject \\(0x[a-zA-B0-9]+\\)\\] "
+        "\\(.+ctkVTKErrorLogModelFileLoggingTest1\\.cpp\\:%2\\) \\- %3$");
+
+  {
+    int entryIndex = 0;
+    QRegExp regexp(expectedLogEntryPatternTemplate.arg("WARNING").arg(66).arg("This is a VTK warning message"));
+    if (!regexp.exactMatch(logLines.at(entryIndex)))
+      {
+      printErrorMessage(
+            QString("Line %1 - Log entry %2 does NOT math expected regular expression.\n\tLogEntry: %3\n\tRegExp: %4").
+                arg(__LINE__).arg(entryIndex).arg(logLines.at(entryIndex)).arg(regexp.pattern()));
+      return EXIT_FAILURE;
+      }
+  }
+  {
+    int entryIndex = 1;
+    QRegExp regexp(expectedLogEntryPatternTemplate.arg("ERROR").arg(67).arg("This is a VTK error message"));
+    if (!regexp.exactMatch(logLines.at(entryIndex)))
+      {
+      printErrorMessage(
+            QString("Line %1 - Log entry %2 does NOT math expected regular expression.\n\tLogEntry: %3\n\tRegExp: %4").
+                arg(__LINE__).arg(entryIndex).arg(logLines.at(entryIndex)).arg(regexp.pattern()));
+      return EXIT_FAILURE;
+      }
+  }
+
   return EXIT_SUCCESS;
 }

+ 19 - 0
Libs/Widgets/Testing/Cpp/ctkErrorLogModelTestHelper.cpp

@@ -24,6 +24,7 @@
 #include <QMutexLocker>
 #include <QSharedPointer>
 #include <QStringList>
+#include <QTextStream>
 #include <QTimer>
 #include <QThread>
 
@@ -168,6 +169,24 @@ void appendToFile(const QString& fileName, const QString& text)
 }
 
 //-----------------------------------------------------------------------------
+QStringList readFile(const QString& filePath)
+{
+  QStringList lines;
+  QFile file(filePath);
+  if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
+    {
+    return lines;
+    }
+   QTextStream in(&file);
+   while(!in.atEnd())
+     {
+     lines << in.readLine();
+     }
+   file.close();
+   return lines;
+}
+
+//-----------------------------------------------------------------------------
 class LogMessageThread : public QThread
 {
 public:

+ 12 - 6
Libs/Widgets/ctkErrorLogModel.cpp

@@ -31,6 +31,7 @@
 #include <QThread>
 
 // CTK includes
+#include "ctkErrorLogContext.h"
 #include "ctkErrorLogModel.h"
 #include "ctkErrorLogAbstractMessageHandler.h"
 #include "ctkFileLogger.h"
@@ -79,12 +80,13 @@ public:
 ctkErrorLogModelPrivate::ctkErrorLogModelPrivate(ctkErrorLogModel& object)
   : q_ptr(&object)
 {
+  qRegisterMetaType<ctkErrorLogContext>("ctkErrorLogContext");
   this->StandardItemModel.setColumnCount(ctkErrorLogModel::MaxColumn);
   this->LogEntryGrouping = false;
   this->AsynchronousLogging = true;
   this->AddingEntry = false;
   this->FileLogger.setEnabled(false);
-  this->FileLoggingPattern = "%{level}: %{timestamp}: %{msg}";
+  this->FileLoggingPattern = "[%{level}][%{origin}] %{timestamp} [%{category}] (%{file}:%{line}) - %{msg}";
 }
 
 // --------------------------------------------------------------------------
@@ -121,8 +123,8 @@ void ctkErrorLogModelPrivate::setMessageHandlerConnection(
   msgHandler->disconnect();
 
   QObject::connect(msgHandler,
-        SIGNAL(messageHandled(QDateTime,QString,ctkErrorLogLevel::LogLevel,QString,QString)),
-        q, SLOT(addEntry(QDateTime,QString,ctkErrorLogLevel::LogLevel,QString,QString)),
+        SIGNAL(messageHandled(QDateTime,QString,ctkErrorLogLevel::LogLevel,QString,ctkErrorLogContext,QString)),
+        q, SLOT(addEntry(QDateTime,QString,ctkErrorLogLevel::LogLevel,QString,ctkErrorLogContext,QString)),
         asynchronous ? Qt::QueuedConnection : Qt::BlockingQueuedConnection);
 }
 
@@ -265,7 +267,7 @@ void ctkErrorLogModel::setTerminalOutputs(
 //------------------------------------------------------------------------------
 void ctkErrorLogModel::addEntry(const QDateTime& currentDateTime, const QString& threadId,
                                 ctkErrorLogLevel::LogLevel logLevel,
-                                const QString& origin, const QString& text)
+                                const QString& origin, const ctkErrorLogContext &context, const QString &text)
 {
   Q_D(ctkErrorLogModel);
 
@@ -363,8 +365,12 @@ void ctkErrorLogModel::addEntry(const QDateTime& currentDateTime, const QString&
   fileLogText.replace("%{origin}", origin);
   fileLogText.replace("%{pid}", QString("%1").arg(QCoreApplication::applicationPid()));
   fileLogText.replace("%{threadid}", threadId);
-  fileLogText.replace("%{msg}", text);
-  d->FileLogger.logMessage(fileLogText);
+  fileLogText.replace("%{function}", context.Function);
+  fileLogText.replace("%{line}", QString("%1").arg(context.Line));
+  fileLogText.replace("%{file}", context.File);
+  fileLogText.replace("%{category}", context.Category);
+  fileLogText.replace("%{msg}", context.Message);
+  d->FileLogger.logMessage(fileLogText.trimmed());
 
   emit this->entryAdded(logLevel);
 }

+ 3 - 2
Libs/Widgets/ctkErrorLogModel.h

@@ -29,10 +29,10 @@
 #include "ctkErrorLogLevel.h"
 #include "ctkErrorLogTerminalOutput.h"
 
-
 //------------------------------------------------------------------------------
 class ctkErrorLogAbstractMessageHandler;
 class ctkErrorLogModelPrivate;
+struct ctkErrorLogContext;
 
 //------------------------------------------------------------------------------
 /// \ingroup Widgets
@@ -135,7 +135,8 @@ public Q_SLOTS:
 
   /// \sa logEntryGrouping(), asynchronousLogging()
   void addEntry(const QDateTime& currentDateTime, const QString& threadId,
-                ctkErrorLogLevel::LogLevel logLevel, const QString& origin, const QString& text);
+                ctkErrorLogLevel::LogLevel logLevel, const QString& origin,
+                const ctkErrorLogContext &context, const QString& text);
 
 Q_SIGNALS:
   void logLevelFilterChanged();

+ 2 - 1
Libs/Widgets/ctkErrorLogStatusMessageHandler.cpp

@@ -27,6 +27,7 @@
 #include <QThread>
 
 // CTK includes
+#include "ctkErrorLogContext.h"
 #include "ctkErrorLogStatusMessageHandler.h"
 #include "ctkUtils.h"
 
@@ -86,5 +87,5 @@ void ctkErrorLogStatusMessageHandler::statusBarMessageChanged(const QString& tex
     }
   this->handleMessage(
         ctk::qtHandleToString(QThread::currentThreadId()),
-        ctkErrorLogLevel::Status, this->handlerPrettyName(), text);
+        ctkErrorLogLevel::Status, this->handlerPrettyName(), ctkErrorLogContext(text), text);
 }