changeset 3359:815b81142ff7 emscripten-logging

Enable custom logging functions to redirect to emscripten specific logging calls in the ORTHANC_ENABLE_LOGGING_STDIO mode.
author Benjamin Golinvaux <bgo@osimis.io>
date Tue, 07 May 2019 11:23:11 +0200
parents eb18269de57f
children ea07b29c6d0e
files Core/Logging.cpp Core/Logging.h Core/LoggingUtils.h LinuxCompilation.txt UnitTestsSources/LoggingTests.cpp
diffstat 5 files changed, 196 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- a/Core/Logging.cpp	Wed Apr 24 07:51:48 2019 +0200
+++ b/Core/Logging.cpp	Tue May 07 11:23:11 2019 +0200
@@ -63,7 +63,17 @@
     void EnableTraceLevel(bool enabled)
     {
     }
+    
+    bool IsTraceLevelEnabled()
+    {
+      return false;
+    }
 
+    bool IsInfoLevelEnabled()
+    {
+      return false;
+    }
+    
     void SetTargetFile(const std::string& path)
     {
     }
@@ -140,12 +150,18 @@
 #elif ORTHANC_ENABLE_LOGGING_STDIO == 1
 
 /*********************************************************
- * Logger compatible with <stdio.h>
+ * Logger compatible with <stdio.h> OR logger that sends its
+ * output to the emscripten html5 api (depending on the 
+ * definition of __EMSCRIPTEN__)
  *********************************************************/
 
 #include <stdio.h>
 #include <boost/lexical_cast.hpp>
 
+#ifdef __EMSCRIPTEN__
+#include "emscripten/html5.h"
+#endif
+
 namespace Orthanc
 {
   namespace Logging
@@ -153,6 +169,67 @@
     static bool globalVerbose_ = false;
     static bool globalTrace_ = false;
     
+#ifdef __EMSCRIPTEN__
+    void defaultErrorLogFunc(const char* msg)
+    {
+      emscripten_console_error(msg);
+    }
+
+    void defaultWarningLogFunc(const char* msg)
+    {
+      emscripten_console_warn(msg);
+    }
+
+    void defaultInfoLogFunc(const char* msg)
+    {
+      emscripten_console_log(msg);
+    }
+
+    void defaultTraceLogFunc(const char* msg)
+    {
+      emscripten_console_log(msg);
+    }
+#else
+// __EMSCRIPTEN__ not #defined
+    void defaultErrorLogFunc(const char* msg)
+    {
+      fprintf(stderr, "E: %s\n", msg);
+    }
+
+    void defaultWarningLogFunc(const char*)
+    {
+      fprintf(stdout, "W: %s\n", msg);
+    }
+
+    void defaultInfoLogFunc(const char*)
+    {
+      fprintf(stdout, "I: %s\n", msg);
+    }
+
+    void defaultTraceLogFunc(const char*)
+    {
+      fprintf(stdout, "T: %s\n", msg);
+    }
+#endif 
+// __EMSCRIPTEN__
+
+    static LoggingFunction globalErrorLogFunc = defaultErrorLogFunc;
+    static LoggingFunction globalWarningLogFunc = defaultWarningLogFunc;
+    static LoggingFunction globalInfoLogFunc = defaultInfoLogFunc;
+    static LoggingFunction globalTraceLogFunc = defaultTraceLogFunc;
+
+    void SetErrorWarnInfoTraceLoggingFunctions(
+      LoggingFunction errorLogFunc,
+      LoggingFunction warningLogfunc,
+      LoggingFunction infoLogFunc,
+      LoggingFunction traceLogFunc)
+    {
+      globalErrorLogFunc = errorLogFunc;
+      globalWarningLogFunc = warningLogfunc;
+      globalInfoLogFunc = infoLogFunc;
+      globalTraceLogFunc = traceLogFunc;
+    }
+
     InternalLogger::InternalLogger(InternalLevel level,
                                    const char* file  /* ignored */,
                                    int line  /* ignored */) :
@@ -165,29 +242,35 @@
       switch (level_)
       {
         case InternalLevel_ERROR:
-          fprintf(stderr, "E: %s\n", message_.c_str());
+          globalErrorLogFunc(message_.c_str());
           break;
 
         case InternalLevel_WARNING:
-          fprintf(stdout, "W: %s\n", message_.c_str());
+          globalWarningLogFunc(message_.c_str());
           break;
 
         case InternalLevel_INFO:
           if (globalVerbose_)
           {
-            fprintf(stdout, "I: %s\n", message_.c_str());
+            globalInfoLogFunc(message_.c_str());
+            // TODO: stone_console_info(message_.c_str());
           }
           break;
 
         case InternalLevel_TRACE:
           if (globalTrace_)
           {
-            fprintf(stdout, "T: %s\n", message_.c_str());
+            globalTraceLogFunc(message_.c_str());
           }
           break;
 
         default:
-          fprintf(stderr, "Unknown log level (%d) for message: %s\n", level_, message_.c_str());
+        {
+          std::stringstream ss;
+          ss << "Unknown log level (" << level_ << ") for message: " << message_;
+          auto s = ss.str();
+          globalErrorLogFunc(s.c_str());
+        }
       }
     }
 
@@ -196,10 +279,21 @@
       globalVerbose_ = enabled;
     }
 
+    bool IsInfoLevelEnabled()
+    {
+      return globalVerbose_;
+    }
+
     void EnableTraceLevel(bool enabled)
     {
       globalTrace_ = enabled;
     }
+
+    bool IsTraceLevelEnabled()
+    {
+      return globalTrace_;
+    }
+
   }
 }
 
@@ -388,7 +482,7 @@
     {
       LoggingMementoImpl* memento = new LoggingMementoImpl();
 
-      memento->valid_ = TRUE;
+      memento->valid_ = true;
       {
         boost::mutex::scoped_lock lock(loggingMutex_);
         memento->infoEnabled_ = loggingContext_->infoEnabled_;
@@ -409,7 +503,7 @@
         reinterpret_cast<LoggingMementoImpl*>(mementoPtr);
       if (!memento->valid_)
         throw std::runtime_error("Memento already used");
-      memento->valid_ = FALSE;
+      memento->valid_ = false;
       {
         boost::mutex::scoped_lock lock(loggingMutex_);
         loggingContext_.reset(new LoggingContext);
@@ -450,6 +544,14 @@
       }
     }
 
+    bool IsInfoLevelEnable()
+    {
+      boost::mutex::scoped_lock lock(loggingMutex_);
+      assert(loggingContext_.get() != NULL);
+
+      return loggingContext_->infoEnabled_;
+    }
+
     void EnableTraceLevel(bool enabled)
     {
       boost::mutex::scoped_lock lock(loggingMutex_);
@@ -464,6 +566,14 @@
       }
     }
 
+    bool IsTraceLevelEnable()
+    {
+      boost::mutex::scoped_lock lock(loggingMutex_);
+      assert(loggingContext_.get() != NULL);
+
+      return loggingContext_->traceEnabled_;
+    }
+
 
     static void CheckFile(std::auto_ptr<std::ofstream>& f)
     {
@@ -664,35 +774,6 @@
       }
     }
 
-
- 
-
-    /*
-    struct FunctionCallingStream : std::ostream, std::streambuf
-    {
-      template<typename T>
-      FunctionCallingStream(T func) : std::ostream(this), func_(func) {}
-
-      int overflow(int c)
-      {
-        if (c != '\n')
-        {
-          currentLine_
-        }
-        else
-        {
-          func_(currentLine_.str().c_str());
-          currentLine_.str("");
-          currentLine_.clear("");
-        }
-        return 0;
-      }
-
-    private:
-      std::stringstream currentLine_;
-    };
-    */
-
     void SetErrorWarnInfoLoggingStreams(std::ostream* errorStream,
       std::ostream* warningStream,
       std::ostream* infoStream)
--- a/Core/Logging.h	Wed Apr 24 07:51:48 2019 +0200
+++ b/Core/Logging.h	Tue May 07 11:23:11 2019 +0200
@@ -29,8 +29,6 @@
  * You should have received a copy of the GNU General Public License
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
-
-
 #pragma once
 
 #include <iostream>
@@ -81,18 +79,24 @@
 
     void EnableTraceLevel(bool enabled);
 
-#ifdef __EMSCRIPTEN__
-    // calling this function will change the error_, warning_ and info_ 
-    // stream objects so that their operator<< writes into the browser 
-    // console using emscripten_console_error(), emscripten_console_warn()
-    // and emscripten_console_log()
-    void EnableEmscriptenLogging();
-#endif
+    bool IsTraceLevelEnabled();
+
+    bool IsInfoLevelEnabled();
 
     void SetTargetFile(const std::string& path);
 
     void SetTargetFolder(const std::string& path);
 
+#if ORTHANC_ENABLE_LOGGING_STDIO == 1
+    typedef void (*LoggingFunction)(const char*);
+    void SetErrorWarnInfoTraceLoggingFunctions(
+      LoggingFunction errorLogFunc,
+      LoggingFunction warningLogfunc,
+      LoggingFunction infoLogFunc,
+      LoggingFunction traceLogFunc);
+#endif
+
+
     struct NullStream : public std::ostream 
     {
       NullStream() : 
@@ -110,13 +114,11 @@
   }
 }
 
-
 #if ORTHANC_ENABLE_LOGGING != 1
 
 #  define LOG(level)   ::Orthanc::Logging::NullStream()
 #  define VLOG(level)  ::Orthanc::Logging::NullStream()
 
-
 #elif (ORTHANC_ENABLE_LOGGING_PLUGIN == 1 ||    \
        ORTHANC_ENABLE_LOGGING_STDIO == 1)
 
@@ -233,27 +235,6 @@
     void DiscardLoggingMemento(LoggingMemento memento);
 
     /**
-      std::streambuf subclass used in FunctionCallingStream
-    */
-    template<typename T>
-    class FuncStreamBuf : public std::stringbuf
-    {
-    public:
-      FuncStreamBuf(T func) : func_(func) {}
-
-      virtual int sync()
-      {
-        std::string text = this->str();
-        const char* buf = text.c_str();
-        func_(buf);
-        this->str("");
-        return 0;
-      }
-    private:
-      T func_;
-    };
-
-    /**
       Set custom logging streams for the error, warning and info logs.
       This function may not be called if a log file or folder has been 
       set beforehand. All three pointers must be valid and cannot be NULL.
@@ -268,6 +249,19 @@
     void SetErrorWarnInfoLoggingStreams(std::ostream* errorStream,
                                         std::ostream* warningStream, 
                                         std::ostream* infoStream);
+
+#ifdef __EMSCRIPTEN__
+    /**
+      This function will change the logging streams so that the logging functions 
+      provided by emscripten html5.h API functions are used : it will change the 
+      error_, warning_ and info_  stream objects so that their operator<< writes 
+      into the browser console using emscripten_console_error(), 
+      emscripten_console_warn() and emscripten_console_log(). This will allow for
+      logging levels to be correctly handled by the browser when the code executes
+      in Web Assembly
+    */
+    void EnableEmscriptenLogging();
+#endif
   }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/LoggingUtils.h	Tue May 07 11:23:11 2019 +0200
@@ -0,0 +1,30 @@
+#include <sstream>
+#include <iostream>
+
+namespace Orthanc
+{
+  namespace Logging
+  {
+
+    /**
+      std::streambuf subclass used in FunctionCallingStream
+    */
+    template<typename T>
+    class FuncStreamBuf : public std::stringbuf
+    {
+    public:
+      FuncStreamBuf(T func) : func_(func) {}
+
+      virtual int sync()
+      {
+        std::string text = this->str();
+        const char* buf = text.c_str();
+        func_(buf);
+        this->str("");
+        return 0;
+      }
+    private:
+      T func_;
+    };
+  }
+}
\ No newline at end of file
--- a/LinuxCompilation.txt	Wed Apr 24 07:51:48 2019 +0200
+++ b/LinuxCompilation.txt	Tue May 07 11:23:11 2019 +0200
@@ -99,21 +99,19 @@
 ------------------------------
 
 # sudo apt-get install build-essential unzip cmake mercurial \
-       	       	       uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \
-       	       	       libgtest-dev libpng-dev libsqlite3-dev libssl-dev libjpeg-dev \
-		       zlib1g-dev libdcmtk2-dev libboost1.48-all-dev libwrap0-dev \
+                       uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \
+                       libgtest-dev libpng-dev libsqlite3-dev libssl-dev libjpeg-dev \
+                       zlib1g-dev libdcmtk2-dev libboost1.48-all-dev libwrap0-dev \
                        libcharls-dev
 
 # cmake "-DDCMTK_LIBRARIES=boost_locale;CharLS;dcmjpls;wrap;oflog" \
         -DALLOW_DOWNLOADS=ON \
-	-DUSE_SYSTEM_CIVETWEB=OFF \
-	-DUSE_SYSTEM_JSONCPP=OFF \
-	-DUSE_SYSTEM_PUGIXML=OFF \
+        -DUSE_SYSTEM_CIVETWEB=OFF \
+        -DUSE_SYSTEM_JSONCPP=OFF \
+        -DUSE_SYSTEM_PUGIXML=OFF \
         -DUSE_GOOGLE_TEST_DEBIAN_PACKAGE=ON \
         -DCMAKE_BUILD_TYPE=Release \
-	~/Orthanc
-
-
+        ~/Orthanc
 
 SUPPORTED - Ubuntu 14.04 LTS and 16.04 LTS
 ------------------------------------------
--- a/UnitTestsSources/LoggingTests.cpp	Wed Apr 24 07:51:48 2019 +0200
+++ b/UnitTestsSources/LoggingTests.cpp	Tue May 07 11:23:11 2019 +0200
@@ -36,6 +36,7 @@
 #include <sstream>
 
 #include "../Core/Logging.h"
+#include "../Core/LoggingUtils.h"
 
 using namespace Orthanc::Logging;
 
@@ -66,11 +67,19 @@
 
 If the log line cannot be matched, the function returns false.
 */
+
+#ifdef WIN32
+# define EOLSTRING "\r\n"
+#else 
+# define EOLSTRING "\n"
+#endif
+
 static bool GetLogLinePayload(std::string& payload,
   const std::string& logLine)
 {
   const char* regexStr = "[A-Z][0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{6} "
-    "[a-zA-Z\\.\\-_]+:[0-9]+\\] (.*)\r\n$";
+    "[a-zA-Z\\.\\-_]+:[0-9]+\\] (.*)" EOLSTRING "$";
+
   boost::regex regexObj(regexStr);
 
   //std::stringstream regexSStr;
@@ -109,7 +118,7 @@
 {
   LoggingMementoScope loggingConfiguration;
 
-  EnableTraceLevel(TRUE);
+  EnableTraceLevel(true);
 
   typedef void(*LoggingFunctionFunc)(const char*);
 
@@ -132,6 +141,7 @@
     testErrorStream.clear();
     std::string payload;
     bool ok = GetLogLinePayload(payload, logLine);
+    ASSERT_TRUE(ok);
     ASSERT_STREQ(payload.c_str(), text);
   }
 
@@ -144,6 +154,7 @@
     testErrorStream.clear();
     std::string payload;
     bool ok = GetLogLinePayload(payload, logLine);
+    ASSERT_TRUE(ok);
     ASSERT_STREQ(payload.c_str(), text);
   }
 
@@ -155,6 +166,7 @@
     testWarningStream.clear();
     std::string payload;
     bool ok = GetLogLinePayload(payload, logLine);
+    ASSERT_TRUE(ok);
     ASSERT_STREQ(payload.c_str(), text);
   }
 
@@ -166,6 +178,7 @@
     testInfoStream.clear();
     std::string payload;
     bool ok = GetLogLinePayload(payload, logLine);
+    ASSERT_TRUE(ok);
     ASSERT_STREQ(payload.c_str(), text);
   }
 }