changeset 5561:0b18690c1935

SDK: added OrthancPluginLogMessage to display plugin name + file and line from plugin
author Alain Mazy <am@orthanc.team>
date Tue, 23 Apr 2024 09:34:02 +0200
parents c80dbbae3f60
children fa000bd60bbe e02cdf358905
files NEWS OrthancFramework/Sources/Logging.cpp OrthancFramework/Sources/Logging.h OrthancServer/CMakeLists.txt OrthancServer/Plugins/Engine/PluginsManager.cpp OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h OrthancServer/Plugins/Samples/DelayedDeletion/Plugin.cpp OrthancServer/Plugins/Samples/Housekeeper/Plugin.cpp OrthancServer/Plugins/Samples/ModalityWorklists/Plugin.cpp OrthancServer/Plugins/Samples/MultitenantDicom/Plugin.cpp OrthancServer/Plugins/Samples/ServeFolders/Plugin.cpp TODO
diffstat 14 files changed, 390 insertions(+), 186 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Mon Apr 22 10:50:33 2024 +0200
+++ b/NEWS	Tue Apr 23 09:34:02 2024 +0200
@@ -34,6 +34,10 @@
 * Housekeeper plugin: Added an option "LimitMainDicomTagsReconstructLevel"
   (allowed values: "Patient", "Study", "Series", "Instance").  This can greatly speed
   up the housekeeper process e.g. if you have only update the Study level ExtraMainDicomTags.
+* SDK: added OrthancPluginLogMessage that is a new primitive for plugins to log messages.
+  This new primitive will display the plugin name, plugin file name and plugin line number
+  in the logs.  If they are using the OrthancFramework, plugins should now use LOG(INFO),
+  LOG(WARNING) and LOG(ERROR) to log their messages.
 
 
 Version 1.12.3 (2024-01-31)
--- a/OrthancFramework/Sources/Logging.cpp	Mon Apr 22 10:50:33 2024 +0200
+++ b/OrthancFramework/Sources/Logging.cpp	Tue Apr 23 09:34:02 2024 +0200
@@ -311,6 +311,10 @@
     {
     }
 
+    void InitializePluginContext(void* pluginContext, const char* pluginName)
+    {
+    }
+
     void Initialize()
     {
     }
@@ -439,6 +443,10 @@
     {
     }
 
+    void InitializePluginContext(void* pluginContext, const char* pluginName)
+    {
+    }
+
     void Initialize()
     {
     }
@@ -488,6 +496,7 @@
     _OrthancPluginService_LogInfo = 1,
     _OrthancPluginService_LogWarning = 2,
     _OrthancPluginService_LogError = 3,
+    _OrthancPluginService_LogMessage = 45,
     _OrthancPluginService_INTERNAL = 0x7fffffff
   } _OrthancPluginService;
 
@@ -500,6 +509,17 @@
                                    _OrthancPluginService service,
                                    const void* params);
   } OrthancPluginContext;
+
+  typedef struct
+  {
+    const char*               message;
+    const char*               plugin;
+    const char*               file;
+    uint32_t                  line;
+    uint32_t                  category;  // can be a LogCategory or a OrthancPluginLogCategory
+    uint32_t                  level;     // can be a LogLevel or a OrthancPluginLogLevel
+  } _OrthancPluginLogMessage;
+
 }
   
 
@@ -539,7 +559,8 @@
 static std::unique_ptr<LoggingStreamsContext>   loggingStreamsContext_;
 static boost::mutex                             loggingStreamsMutex_;
 static Orthanc::Logging::NullStream             nullStream_;
-static OrthancPluginContext*                    pluginContext_ = NULL;
+static OrthancPluginContext*                    pluginContext_ = NULL;    // this is != NULL only when running from a plugin
+static const char*                              pluginName_ = NULL;       // this is != NULL only when running from a plugin
 static boost::recursive_mutex                   threadNamesMutex_;
 static std::map<boost::thread::id, std::string> threadNames_;
 static bool                                     enableThreadNames_ = true;
@@ -667,6 +688,7 @@
 
     static void GetLinePrefix(std::string& prefix,
                               LogLevel level,
+                              const char* pluginName,  // when logging in the core but coming from a plugin, pluginName_ is NULL but this argument is != NULL
                               const char* file,
                               int line,
                               LogCategory category)
@@ -740,7 +762,13 @@
         threadName[0] = '\0';
       }
 
-      prefix = (std::string(date) + threadName + path.filename().string() + ":" +
+      std::string internalPluginName = "";
+      if (pluginName != NULL)
+      {
+        internalPluginName = std::string(pluginName) + ":/";
+      }
+
+      prefix = (std::string(date) + threadName + internalPluginName + path.filename().string() + ":" +
                 boost::lexical_cast<std::string>(line) + "] ");
 
       if (level != LogLevel_ERROR &&
@@ -763,6 +791,12 @@
       EnableInfoLevel(true);  // allow the plugin to log at info level (but the Orthanc Core still decides of the level)
     }
 
+    void InitializePluginContext(void* pluginContext, const char* pluginName)
+    {
+      InitializePluginContext(pluginContext);
+      pluginName_ = pluginName;
+    }
+
 
     void Initialize()
     {
@@ -837,6 +871,7 @@
 
 
     void InternalLogger::Setup(LogCategory category,
+                               const char* pluginName,
                                const char* file,
                                int line)
     {
@@ -869,7 +904,7 @@
         }
 
         std::string prefix;
-        GetLinePrefix(prefix, level_, file, line, category);
+        GetLinePrefix(prefix, level_, pluginName, file, line, category);
 
         {
           // We lock the global mutex. The mutex is locked until the
@@ -934,20 +969,42 @@
                                    int line) :
       lock_(loggingStreamsMutex_, boost::defer_lock_t()),
       level_(level),
-      stream_(&nullStream_)  // By default, logging to "/dev/null" is simulated
+      stream_(&nullStream_),  // By default, logging to "/dev/null" is simulated
+      category_(category),
+      file_(file),
+      line_(line)
     {
-      Setup(category, file, line);
+      Setup(category, NULL, file, line);
     }
 
+    InternalLogger::InternalLogger(LogLevel level,
+                                   LogCategory category,
+                                   const char* pluginName,
+                                   const char* file,
+                                   int line) :
+      lock_(loggingStreamsMutex_, boost::defer_lock_t()),
+      level_(level),
+      stream_(&nullStream_),  // By default, logging to "/dev/null" is simulated
+      category_(category),
+      file_(file),
+      line_(line)
+    {
+      Setup(category, pluginName, file, line);
+    }
+
+
 
     InternalLogger::InternalLogger(LogLevel level,
                                    const char* file,
                                    int line) :
       lock_(loggingStreamsMutex_, boost::defer_lock_t()),
       level_(level),
-      stream_(&nullStream_)  // By default, logging to "/dev/null" is simulated
+      stream_(&nullStream_),  // By default, logging to "/dev/null" is simulated
+      category_(LogCategory_GENERIC),
+      file_(file),
+      line_(line)
     {
-      Setup(LogCategory_GENERIC, file, line);
+      Setup(LogCategory_GENERIC, NULL, file, line);
     }
 
 
@@ -961,22 +1018,36 @@
 
         if (pluginContext_ != NULL)
         {
-          switch (level_)
+          if (pluginName_ != NULL) // this shall happen only if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 12, 4) && ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 4)
           {
-            case LogLevel_ERROR:
-              pluginContext_->InvokeService(pluginContext_, _OrthancPluginService_LogError, message.c_str());
-              break;
+            _OrthancPluginLogMessage m;
+            m.category = category_;
+            m.level = level_;
+            m.file = file_;
+            m.line = line_;
+            m.plugin = pluginName_;
+            m.message = message.c_str();
+            pluginContext_->InvokeService(pluginContext_, _OrthancPluginService_LogMessage, &m);
+          }
+          else
+          {
+            switch (level_)
+            {
+              case LogLevel_ERROR:
+                pluginContext_->InvokeService(pluginContext_, _OrthancPluginService_LogError, message.c_str());
+                break;
 
-            case LogLevel_WARNING:
-              pluginContext_->InvokeService(pluginContext_, _OrthancPluginService_LogWarning, message.c_str());
-              break;
+              case LogLevel_WARNING:
+                pluginContext_->InvokeService(pluginContext_, _OrthancPluginService_LogWarning, message.c_str());
+                break;
 
-            case LogLevel_INFO:
-              pluginContext_->InvokeService(pluginContext_, _OrthancPluginService_LogInfo, message.c_str());
-              break;
+              case LogLevel_INFO:
+                pluginContext_->InvokeService(pluginContext_, _OrthancPluginService_LogInfo, message.c_str());
+                break;
 
-            default:
-              break;
+              default:
+                break;
+            }
           }
         }
       }
--- a/OrthancFramework/Sources/Logging.h	Mon Apr 22 10:50:33 2024 +0200
+++ b/OrthancFramework/Sources/Logging.h	Tue Apr 23 09:34:02 2024 +0200
@@ -46,12 +46,13 @@
 {
   namespace Logging
   {
+    // Note: these values must match the ones in OrthancCPlugin.h
     enum LogLevel
     {
-      LogLevel_ERROR,
-      LogLevel_WARNING,
-      LogLevel_INFO,
-      LogLevel_TRACE
+      LogLevel_ERROR     = 0,
+      LogLevel_WARNING   = 1,
+      LogLevel_INFO      = 2,
+      LogLevel_TRACE     = 3
     };
 
     /**
@@ -59,6 +60,7 @@
      * mask. As a consequence, there can be up to 31 log categories
      * (not 32, as the value GENERIC is reserved for the log commands
      * that don't fall in a specific category).
+     * Note: these values must match the ones in OrthancCPlugin.h
      **/
     enum LogCategory
     {
@@ -78,6 +80,9 @@
     // "pluginContext" must be of type "OrthancPluginContext"
     ORTHANC_PUBLIC void InitializePluginContext(void* pluginContext);
 
+    // note: this variant shall be called only from a plugin and only if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 12, 4) && ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 4)
+    ORTHANC_PUBLIC void InitializePluginContext(void* pluginContext, const char* pluginName);
+
     ORTHANC_PUBLIC void Initialize();
 
     ORTHANC_PUBLIC void Finalize();
@@ -162,6 +167,7 @@
 #  define LOG(level)            ::Orthanc::Logging::NullStream()
 #  define VLOG(unused)          ::Orthanc::Logging::NullStream()
 #  define CLOG(level, category) ::Orthanc::Logging::NullStream()
+#  define LOG_FROM_PLUGIN(level, category, pluginName, file, line)  ::Orthanc::Logging::NullStream()
 #else /* ORTHANC_ENABLE_LOGGING == 1 */
 
 #if !defined(__ORTHANC_FILE__)
@@ -182,6 +188,8 @@
 #  define CLOG(level, category) ::Orthanc::Logging::InternalLogger      \
   (::Orthanc::Logging::LogLevel_ ## level,                              \
    ::Orthanc::Logging::LogCategory_ ## category, __ORTHANC_FILE__, __LINE__)
+#  define LOG_FROM_PLUGIN(level, category, pluginName, file, line)  ::Orthanc::Logging::InternalLogger      \
+  (level, category, pluginName, file, line)
 #endif
 
 
@@ -258,8 +266,12 @@
       LogLevel                            level_;
       std::unique_ptr<std::stringstream>  pluginStream_;
       std::ostream*                       stream_;
+      LogCategory                         category_;
+      const char*                         file_;
+      uint32_t                            line_;
 
       void Setup(LogCategory category,
+                 const char* pluginName,
                  const char* file,
                  int line);
 
@@ -269,6 +281,12 @@
                      const char* file,
                      int line);
 
+      InternalLogger(LogLevel level,
+                     LogCategory category,
+                     const char* pluginName,
+                     const char* file,
+                     int line);
+
       // For backward binary compatibility with Orthanc Framework <= 1.8.0
       InternalLogger(LogLevel level,
                      const char* file,
--- a/OrthancServer/CMakeLists.txt	Mon Apr 22 10:50:33 2024 +0200
+++ b/OrthancServer/CMakeLists.txt	Tue Apr 23 09:34:02 2024 +0200
@@ -496,6 +496,10 @@
     
     ${CMAKE_SOURCE_DIR}/../OrthancFramework/Resources/ThirdParty/base64/base64.cpp
     ${CMAKE_SOURCE_DIR}/../OrthancFramework/Resources/ThirdParty/md5/md5.c
+
+    # the orthanc framework sources
+    ${ORTHANC_CORE_SOURCES}
+
     Plugins/Samples/Common/OrthancPluginCppWrapper.cpp
     )
 
@@ -520,6 +524,8 @@
 
   DefineSourceBasenameForTarget(PluginsDependencies)
 
+  target_include_directories(PluginsDependencies PUBLIC ${CMAKE_SOURCE_DIR}/../OrthancFramework/Sources)
+
   # Add the "-fPIC" option as this static library must be embedded
   # inside shared libraries (important on UNIX)
   set_target_properties(
--- a/OrthancServer/Plugins/Engine/PluginsManager.cpp	Mon Apr 22 10:50:33 2024 +0200
+++ b/OrthancServer/Plugins/Engine/PluginsManager.cpp	Tue Apr 23 09:34:02 2024 +0200
@@ -161,6 +161,18 @@
         CLOG(INFO, PLUGINS) << reinterpret_cast<const char*>(params);
         return OrthancPluginErrorCode_Success;
 
+      case _OrthancPluginService_LogMessage:
+        {
+          const _OrthancPluginLogMessage& m = *reinterpret_cast<const _OrthancPluginLogMessage*>(params);
+          // we may convert directly from OrthancPluginLogLevel to LogLevel (and category) because the enum values must be identical 
+          // for Orthanc::Logging to work both in the core and in the plugins
+          Orthanc::Logging::LogLevel level = static_cast<Orthanc::Logging::LogLevel>(m.level);
+          Orthanc::Logging::LogCategory category = static_cast<Orthanc::Logging::LogCategory>(m.category);
+          
+          LOG_FROM_PLUGIN(level, category, m.plugin, m.file, m.line) << m.message;
+          return OrthancPluginErrorCode_Success;
+        };
+
       default:
         break;
     }
--- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h	Mon Apr 22 10:50:33 2024 +0200
+++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h	Tue Apr 23 09:34:02 2024 +0200
@@ -465,6 +465,7 @@
     _OrthancPluginService_GetDatabaseServerIdentifier = 42,         /* New in Orthanc 1.11.1 */
     _OrthancPluginService_SetMetricsIntegerValue = 43,              /* New in Orthanc 1.12.1 */
     _OrthancPluginService_SetCurrentThreadName = 44,                /* New in Orthanc 1.12.2 */
+    _OrthancPluginService_LogMessage = 45,                          /* New in Orthanc 1.12.4 */
 
 
     /* Registration of callbacks */
@@ -9479,6 +9480,79 @@
     return context->InvokeService(context, _OrthancPluginService_SetCurrentThreadName, threadName);
   }
 
+  typedef enum
+  {
+    // these values must match LogLevel in the Orthanc Core
+    OrthancPluginLogLevel_Error = 0,    /*!< Error log level */
+    OrthancPluginLogLevel_Warning = 1,  /*!< Warning log level */
+    OrthancPluginLogLevel_Info = 2,     /*!< Info log level */
+    OrthancPluginLogLevel_Trace = 3,    /*!< Trace log level */
+  
+    // Force the enum to be 32 bits
+    OrthancPluginLogLevel_INTERNAL = 0x7FFFFFFF
+  } OrthancPluginLogLevel;
+
+  typedef enum
+  {
+    // these values must match LogCategory in the Orthanc Core
+    OrthancPluginLogCategory_Generic = (1 << 0),  /*!< Generic (default) category */
+    OrthancPluginLogCategory_Plugins = (1 << 1),  /*!< Plugin engine related logs (shall not be used by plugins) */
+    OrthancPluginLogCategory_Http    = (1 << 2),  /*!< HTTP related logs */
+    OrthancPluginLogCategory_Sqlite  = (1 << 3),  /*!< SQLite related logs (shall not be used by plugins) */
+    OrthancPluginLogCategory_Dicom   = (1 << 4),  /*!< DICOM related logs */
+    OrthancPluginLogCategory_Jobs    = (1 << 5),  /*!< jobs related logs */
+    OrthancPluginLogCategory_Lua     = (1 << 6),  /*!< Lua related logs (shall not be used by plugins) */
+
+    // Force the enum to be 32 bits
+    OrthancPluginLogCategory_INTERNAL = 0x7FFFFFFF
+  } OrthancPluginLogCategory;
+
+
+  // note: this structure is also defined in Logging.h and it must be binary compatible
+  typedef struct
+  {
+    const char*               message;
+    const char*               plugin;
+    const char*               file;
+    uint32_t                  line;
+    OrthancPluginLogCategory  category;
+    OrthancPluginLogLevel     level;
+  } _OrthancPluginLogMessage;
+
+
+  /**
+   * @brief Log a message.
+   *
+   * Log a message using the Orthanc logging system.
+   * 
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param message The message to be logged.
+   * @param plugin The plugin name.
+   * @param file The filename in the plugin code.
+   * @param line The file line in the plugin code.
+   * @param category The category.
+   * @param level The level of the message.
+   **/
+  ORTHANC_PLUGIN_INLINE void OrthancPluginLogMessage(
+    OrthancPluginContext* context,
+    const char* message,
+    const char* plugin,
+    const char* file,
+    uint32_t line,
+    OrthancPluginLogCategory category,
+    OrthancPluginLogLevel level)
+  {
+    _OrthancPluginLogMessage m;
+    m.message = message;
+    m.plugin = plugin;
+    m.file = file;
+    m.category = category;
+    m.line = line;
+    m.level = level;
+    context->InvokeService(context, _OrthancPluginService_LogMessage, &m);
+  }
+
+
 #ifdef  __cplusplus
 }
 #endif
--- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp	Mon Apr 22 10:50:33 2024 +0200
+++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp	Tue Apr 23 09:34:02 2024 +0200
@@ -79,9 +79,20 @@
     }
   }
 
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 4) && ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 12, 4)
+  static const char* pluginName_ = NULL;
+
+  void SetGlobalContext(OrthancPluginContext* context, const char* pluginName)
+  {
+    SetGlobalContext(context);
+    pluginName_ = pluginName;
+  }
+#endif
+
   void ResetGlobalContext()
   {
     globalContext_ = NULL;
+    pluginName_ = NULL;
   }
 
   bool HasGlobalContext()
@@ -102,6 +113,54 @@
     }
   }
 
+  void LogError(const std::string& message)
+  {
+    if (HasGlobalContext())
+    {
+      OrthancPluginLogError(GetGlobalContext(), message.c_str());
+    }
+  }
+
+  void LogWarning(const std::string& message)
+  {
+    if (HasGlobalContext())
+    {
+      OrthancPluginLogWarning(GetGlobalContext(), message.c_str());
+    }
+  }
+
+  void LogInfo(const std::string& message)
+  {
+    if (HasGlobalContext())
+    {
+      OrthancPluginLogInfo(GetGlobalContext(), message.c_str());
+    }
+  }
+
+
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 4) && ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 12, 4)
+  // This file does not have any dependencies on Logging.h, we must call the "native" plugin service
+
+  void _LogMessage(OrthancPluginLogLevel level, const char* file, uint32_t line, const std::string& message)
+  {
+    if (HasGlobalContext())
+    {
+      OrthancPluginLogMessage(GetGlobalContext(), message.c_str(), pluginName_, file, line, OrthancPluginLogCategory_Generic, level);
+    }
+  }
+
+  #define LOG_ERROR(msg) _LogMessage(OrthancPluginLogLevel_Error, __ORTHANC_FILE__, __LINE__, msg);
+  #define LOG_WARNING(msg) _LogMessage(OrthancPluginLogLevel_Warning, __ORTHANC_FILE__, __LINE__, msg);
+  #define LOG_INFO(msg) _LogMessage(OrthancPluginLogLevel_Info, __ORTHANC_FILE__, __LINE__, msg);
+
+#else
+
+  #define LOG_ERROR(msg) LogError(msg);
+  #define LOG_WARNING(msg) LogWarning(msg);
+  #define LOG_INFO(msg) LogInfo(msg);
+
+#endif
+
 
   void MemoryBuffer::Check(OrthancPluginErrorCode code)
   {
@@ -233,7 +292,7 @@
 
     if (!ReadJson(target, buffer_.data, buffer_.size))
     {
-      LogError("Cannot convert some memory buffer to JSON");
+      LOG_ERROR("Cannot convert some memory buffer to JSON");
       ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
     }
   }
@@ -404,7 +463,7 @@
     }
     else
     {
-      LogError("Cannot parse JSON: " + std::string(err));
+      LOG_ERROR("Cannot parse JSON: " + std::string(err));
       return false;
     }
 #endif
@@ -565,13 +624,13 @@
   {
     if (str_ == NULL)
     {
-      LogError("Cannot convert an empty memory buffer to JSON");
+      LOG_ERROR("Cannot convert an empty memory buffer to JSON");
       ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
     }
 
     if (!ReadJson(target, str_))
     {
-      LogError("Cannot convert some memory buffer to JSON");
+      LOG_ERROR("Cannot convert some memory buffer to JSON");
       ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
     }
   }
@@ -581,13 +640,13 @@
   {
     if (str_ == NULL)
     {
-      LogError("Cannot convert an empty memory buffer to JSON");
+      LOG_ERROR("Cannot convert an empty memory buffer to JSON");
       ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
     }
 
     if (!ReadJsonWithoutComments(target, str_))
     {
-      LogError("Cannot convert some memory buffer to JSON");
+      LOG_ERROR("Cannot convert some memory buffer to JSON");
       ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
     }
   }
@@ -625,7 +684,7 @@
 
     if (body.size() > 0xffffffffu)
     {
-      LogError("Cannot handle body size > 4GB");
+      LOG_ERROR("Cannot handle body size > 4GB");
       ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
     }
 
@@ -645,7 +704,7 @@
 
     if (body.size() > 0xffffffffu)
     {
-      LogError("Cannot handle body size > 4GB");
+      LOG_ERROR("Cannot handle body size > 4GB");
       ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
     }
 
@@ -688,34 +747,6 @@
     }
   }
 
-
-  void LogError(const std::string& message)
-  {
-    if (HasGlobalContext())
-    {
-      OrthancPluginLogError(GetGlobalContext(), message.c_str());
-    }
-  }
-
-
-  void LogWarning(const std::string& message)
-  {
-    if (HasGlobalContext())
-    {
-      OrthancPluginLogWarning(GetGlobalContext(), message.c_str());
-    }
-  }
-
-
-  void LogInfo(const std::string& message)
-  {
-    if (HasGlobalContext())
-    {
-      OrthancPluginLogInfo(GetGlobalContext(), message.c_str());
-    }
-  }
-
-
   void OrthancConfiguration::LoadConfiguration()
   {
     OrthancString str;
@@ -723,7 +754,7 @@
 
     if (str.GetContent() == NULL)
     {
-      LogError("Cannot access the Orthanc configuration");
+      LOG_ERROR("Cannot access the Orthanc configuration");
       ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
     }
 
@@ -731,7 +762,7 @@
 
     if (configuration_.type() != Json::objectValue)
     {
-      LogError("Unable to read the Orthanc configuration");
+      LOG_ERROR("Unable to read the Orthanc configuration");
       ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
     }
   }
@@ -799,8 +830,8 @@
     {
       if (configuration_[key].type() != Json::objectValue)
       {
-        LogError("The configuration section \"" + target.path_ +
-                 "\" is not an associative array as expected");
+        LOG_ERROR("The configuration section \"" + target.path_ +
+                  "\" is not an associative array as expected");
 
         ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
       }
@@ -822,8 +853,8 @@
 
     if (configuration_[key].type() != Json::stringValue)
     {
-      LogError("The configuration option \"" + GetPath(key) +
-               "\" is not a string as expected");
+      LOG_ERROR("The configuration option \"" + GetPath(key) +
+                "\" is not a string as expected");
 
       ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
     }
@@ -854,8 +885,8 @@
         return true;
 
       default:
-        LogError("The configuration option \"" + GetPath(key) +
-                 "\" is not an integer as expected");
+        LOG_ERROR("The configuration option \"" + GetPath(key) +
+                  "\" is not an integer as expected");
 
         ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
     }
@@ -873,8 +904,8 @@
 
     if (tmp < 0)
     {
-      LogError("The configuration option \"" + GetPath(key) +
-               "\" is not a positive integer as expected");
+      LOG_ERROR("The configuration option \"" + GetPath(key) +
+                "\" is not a positive integer as expected");
 
       ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
     }
@@ -898,8 +929,8 @@
 
     if (configuration_[key].type() != Json::booleanValue)
     {
-      LogError("The configuration option \"" + GetPath(key) +
-               "\" is not a Boolean as expected");
+      LOG_ERROR("The configuration option \"" + GetPath(key) +
+                "\" is not a Boolean as expected");
 
       ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
     }
@@ -934,8 +965,8 @@
         return true;
 
       default:
-        LogError("The configuration option \"" + GetPath(key) +
-                 "\" is not an integer as expected");
+        LOG_ERROR("The configuration option \"" + GetPath(key) +
+                  "\" is not an integer as expected");
 
         ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
     }
@@ -994,8 +1025,8 @@
         break;
     }
 
-    LogError("The configuration option \"" + GetPath(key) +
-             "\" is not a list of strings as expected");
+    LOG_ERROR("The configuration option \"" + GetPath(key) +
+              "\" is not a list of strings as expected");
 
     ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
   }
@@ -1115,8 +1146,8 @@
 
     if (configuration_[key].type() != Json::objectValue)
     {
-      LogError("The configuration option \"" + GetPath(key) +
-               "\" is not an object as expected");
+      LOG_ERROR("The configuration option \"" + GetPath(key) +
+                "\" is not an object as expected");
 
       ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
     }
@@ -1133,8 +1164,8 @@
       }
       else
       {
-        LogError("The configuration option \"" + GetPath(key) +
-                 "\" is not a dictionary mapping strings to strings");
+        LOG_ERROR("The configuration option \"" + GetPath(key) +
+                  "\" is not a dictionary mapping strings to strings");
 
         ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
       }
@@ -1156,7 +1187,7 @@
   {
     if (image_ == NULL)
     {
-      LogError("Trying to access a NULL image");
+      LOG_ERROR("Trying to access a NULL image");
       ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
     }
   }
@@ -1182,7 +1213,7 @@
 
     if (image_ == NULL)
     {
-      LogError("Cannot create an image");
+      LOG_ERROR("Cannot create an image");
       ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
     }
   }
@@ -1199,7 +1230,7 @@
 
     if (image_ == NULL)
     {
-      LogError("Cannot create an image accessor");
+      LOG_ERROR("Cannot create an image accessor");
       ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
     }
   }
@@ -1213,7 +1244,7 @@
 
     if (image_ == NULL)
     {
-      LogError("Cannot uncompress a PNG image");
+      LOG_ERROR("Cannot uncompress a PNG image");
       ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
     }
   }
@@ -1226,7 +1257,7 @@
     image_ = OrthancPluginUncompressImage(GetGlobalContext(), data, size, OrthancPluginImageFormat_Jpeg);
     if (image_ == NULL)
     {
-      LogError("Cannot uncompress a JPEG image");
+      LOG_ERROR("Cannot uncompress a JPEG image");
       ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
     }
   }
@@ -1240,7 +1271,7 @@
     image_ = OrthancPluginDecodeDicomImage(GetGlobalContext(), data, size, frame);
     if (image_ == NULL)
     {
-      LogError("Cannot uncompress a DICOM image");
+      LOG_ERROR("Cannot uncompress a DICOM image");
       ORTHANC_PLUGINS_THROW_EXCEPTION(ParameterOutOfRange);
     }
   }
@@ -1654,13 +1685,13 @@
                                    unsigned int minor,
                                    unsigned int revision)
   {
-    LogError("Your version of the Orthanc core (" +
-             std::string(GetGlobalContext()->orthancVersion) +
-             ") is too old to run this plugin (version " +
-             boost::lexical_cast<std::string>(major) + "." +
-             boost::lexical_cast<std::string>(minor) + "." +
-             boost::lexical_cast<std::string>(revision) +
-             " is required)");
+    LOG_ERROR("Your version of the Orthanc core (" +
+              std::string(GetGlobalContext()->orthancVersion) +
+              ") is too old to run this plugin (version " +
+              boost::lexical_cast<std::string>(major) + "." +
+              boost::lexical_cast<std::string>(minor) + "." +
+              boost::lexical_cast<std::string>(revision) +
+              " is required)");
   }
 
   bool CheckMinimalVersion(const char* version,
@@ -1740,7 +1771,7 @@
   {
     if (!HasGlobalContext())
     {
-      LogError("Bad Orthanc context in the plugin");
+      LOG_ERROR("Bad Orthanc context in the plugin");
       return false;
     }
 
@@ -1777,7 +1808,7 @@
     }
     else
     {
-      LogError("Inexistent peer: " + name);
+      LOG_ERROR("Inexistent peer: " + name);
       ORTHANC_PLUGINS_THROW_EXCEPTION(UnknownResource);
     }
   }
@@ -2061,7 +2092,7 @@
 
     if (body.size() > 0xffffffffu)
     {
-      LogError("Cannot handle body size > 4GB");
+      LOG_ERROR("Cannot handle body size > 4GB");
       ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
     }
 
@@ -2098,7 +2129,7 @@
 
     if (body.size() > 0xffffffffu)
     {
-      LogError("Cannot handle body size > 4GB");
+      LOG_ERROR("Cannot handle body size > 4GB");
       ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
     }
 
@@ -2464,7 +2495,7 @@
 
     if (id == NULL)
     {
-      LogError("Plugin cannot submit job");
+      LOG_ERROR("Plugin cannot submit job");
       OrthancPluginFreeJob(GetGlobalContext(), orthanc);
       ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(OrthancPluginErrorCode_Plugin);
     }
@@ -2533,7 +2564,7 @@
           throw Orthanc::OrthancException(static_cast<Orthanc::ErrorCode>(status["ErrorCode"].asInt()),
                                           status["ErrorDescription"].asString());
 #else
-          LogError("Exception while executing the job: " + status["ErrorDescription"].asString());
+          LOG_ERROR("Exception while executing the job: " + status["ErrorDescription"].asString());
           ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(status["ErrorCode"].asInt());          
 #endif
         }
@@ -2558,7 +2589,7 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
                                       "Expected a JSON object in the body");
 #else
-      LogError("Expected a JSON object in the body");
+      LOG_ERROR("Expected a JSON object in the body");
       ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
 #endif
     }
@@ -2574,7 +2605,7 @@
                                         "Option \"" + std::string(KEY_SYNCHRONOUS) +
                                         "\" must be Boolean");
 #else
-        LogError("Option \"" + std::string(KEY_SYNCHRONOUS) + "\" must be Boolean");
+        LOG_ERROR("Option \"" + std::string(KEY_SYNCHRONOUS) + "\" must be Boolean");
         ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
 #endif
       }
@@ -2593,7 +2624,7 @@
                                         "Option \"" + std::string(KEY_ASYNCHRONOUS) +
                                         "\" must be Boolean");
 #else
-        LogError("Option \"" + std::string(KEY_ASYNCHRONOUS) + "\" must be Boolean");
+        LOG_ERROR("Option \"" + std::string(KEY_ASYNCHRONOUS) + "\" must be Boolean");
         ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
 #endif
       }
@@ -2614,7 +2645,7 @@
                                         "Option \"" + std::string(KEY_PRIORITY) +
                                         "\" must be an integer");
 #else
-        LogError("Option \"" + std::string(KEY_PRIORITY) + "\" must be an integer");
+        LOG_ERROR("Option \"" + std::string(KEY_PRIORITY) + "\" must be an integer");
         ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
 #endif
       }
@@ -3135,7 +3166,7 @@
 
     if (body.size() > 0xffffffffu)
     {
-      LogError("Cannot handle body size > 4GB");
+      LOG_ERROR("Cannot handle body size > 4GB");
       ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
     }
 
@@ -3280,7 +3311,7 @@
     
     if (!ReadJson(answerBody, body))
     {
-      LogError("Cannot convert HTTP answer body to JSON");
+      LOG_ERROR("Cannot convert HTTP answer body to JSON");
       ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
     }
   }
--- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Mon Apr 22 10:50:33 2024 +0200
+++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Tue Apr 23 09:34:02 2024 +0200
@@ -137,6 +137,10 @@
 
   void SetGlobalContext(OrthancPluginContext* context);
 
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 4) && ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 12, 4)
+  void SetGlobalContext(OrthancPluginContext* context, const char* pluginName);
+#endif
+
   void ResetGlobalContext();
 
   bool HasGlobalContext();
@@ -637,11 +641,11 @@
   const char* AutodetectMimeType(const std::string& path);
 #endif
 
-  void LogError(const std::string& message);
+  void LogError(const std::string& message);   // From Orthanc 1.12.4, use LOG(ERROR) to display the plugin name, file and line (First set a plugin name in Orthanc::Logging::InitializePluginContext)
 
-  void LogWarning(const std::string& message);
+  void LogWarning(const std::string& message); // From Orthanc 1.12.4, use LOG(WARNING) to display the plugin name, file and line (First set a plugin name in Orthanc::Logging::InitializePluginContext)
 
-  void LogInfo(const std::string& message);
+  void LogInfo(const std::string& message);    // From Orthanc 1.12.4, use LOG(INFO) to display the plugin name, file and line (First set a plugin name in Orthanc::Logging::InitializePluginContext)
 
   void ReportMinimalOrthancVersion(unsigned int major,
                                    unsigned int minor,
--- a/OrthancServer/Plugins/Samples/DelayedDeletion/Plugin.cpp	Mon Apr 22 10:50:33 2024 +0200
+++ b/OrthancServer/Plugins/Samples/DelayedDeletion/Plugin.cpp	Tue Apr 23 09:34:02 2024 +0200
@@ -117,7 +117,7 @@
     // copy from a buffer allocated on plugin's heap into a buffer allocated on core's heap
     if (OrthancPluginCreateMemoryBuffer64(OrthancPlugins::GetGlobalContext(), target, buffer->GetSize()) != OrthancPluginErrorCode_Success)
     {
-      OrthancPlugins::LogError("Delayed deletion plugin: error while reading object " + std::string(uuid) + ", cannot allocate memory of size " + boost::lexical_cast<std::string>(buffer->GetSize()) + " bytes");
+      LOG(ERROR) << "Delayed deletion plugin: error while reading object " << uuid << ", cannot allocate memory of size " << buffer->GetSize() << " bytes";
       return OrthancPluginErrorCode_StorageAreaPlugin;
     }
 
--- a/OrthancServer/Plugins/Samples/Housekeeper/Plugin.cpp	Mon Apr 22 10:50:33 2024 +0200
+++ b/OrthancServer/Plugins/Samples/Housekeeper/Plugin.cpp	Tue Apr 23 09:34:02 2024 +0200
@@ -23,6 +23,7 @@
 #define HOUSEKEEPER_NAME "housekeeper"
 
 #include "../../../../OrthancFramework/Sources/Compatibility.h"
+#include "../../../../OrthancFramework/Sources/Logging.h"
 #include "../Common/OrthancPluginCppWrapper.h"
 
 #include <boost/thread.hpp>
@@ -91,7 +92,7 @@
     }
     else
     {
-      OrthancPlugins::LogWarning("Housekeeper: invalid schedule: unknown 'day': " + weekday);      
+      LOG(WARNING) << "Housekeeper: invalid schedule: unknown 'day': " << weekday;
       ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
     }
 
@@ -403,12 +404,12 @@
   {
     if (triggerOnUnnecessaryDicomAsJsonFiles_)
     {
-      OrthancPlugins::LogWarning("Housekeeper: your storage might still contain some dicom-as-json files -> will perform housekeeping");
+      LOG(WARNING) << "Housekeeper: your storage might still contain some dicom-as-json files -> will perform housekeeping";
       needsReconstruct = true;  // the default reconstruct removes the dicom-as-json
     }
     else
     {
-      OrthancPlugins::LogWarning("Housekeeper: your storage might still contain some dicom-as-json files but the trigger has been disabled");
+      LOG(WARNING) << "Housekeeper: your storage might still contain some dicom-as-json files but the trigger has been disabled";
     }
   }
 
@@ -416,12 +417,12 @@
   {
     if (triggerOnMainDicomTagsChange_)
     {
-      OrthancPlugins::LogWarning("Housekeeper: Patient main dicom tags have changed, -> will perform housekeeping");
+      LOG(WARNING) << "Housekeeper: Patient main dicom tags have changed, -> will perform housekeeping";
       needsReconstruct = true;
     }
     else
     {
-      OrthancPlugins::LogWarning("Housekeeper: Patient main dicom tags have changed but the trigger is disabled");
+      LOG(WARNING) << "Housekeeper: Patient main dicom tags have changed but the trigger is disabled";
     }
   }
 
@@ -429,12 +430,12 @@
   {
     if (triggerOnMainDicomTagsChange_)
     {
-      OrthancPlugins::LogWarning("Housekeeper: Study main dicom tags have changed, -> will perform housekeeping");
+      LOG(WARNING) << "Housekeeper: Study main dicom tags have changed, -> will perform housekeeping";
       needsReconstruct = true;
     }
     else
     {
-      OrthancPlugins::LogWarning("Housekeeper: Study main dicom tags have changed but the trigger is disabled");
+      LOG(WARNING) << "Housekeeper: Study main dicom tags have changed but the trigger is disabled";
     }
   }
 
@@ -442,12 +443,12 @@
   {
     if (triggerOnMainDicomTagsChange_)
     {
-      OrthancPlugins::LogWarning("Housekeeper: Series main dicom tags have changed, -> will perform housekeeping");
+      LOG(WARNING) << "Housekeeper: Series main dicom tags have changed, -> will perform housekeeping";
       needsReconstruct = true;
     }
     else
     {
-      OrthancPlugins::LogWarning("Housekeeper: Series main dicom tags have changed but the trigger is disabled");
+      LOG(WARNING) << "Housekeeper: Series main dicom tags have changed but the trigger is disabled";
     }
   }
 
@@ -455,12 +456,12 @@
   {
     if (triggerOnMainDicomTagsChange_)
     {
-      OrthancPlugins::LogWarning("Housekeeper: Instance main dicom tags have changed, -> will perform housekeeping");
+      LOG(WARNING) << "Housekeeper: Instance main dicom tags have changed, -> will perform housekeeping";
       needsReconstruct = true;
     }
     else
     {
-      OrthancPlugins::LogWarning("Housekeeper: Instance main dicom tags have changed but the trigger is disabled");
+      LOG(WARNING) << "Housekeeper: Instance main dicom tags have changed but the trigger is disabled";
     }
   }
 
@@ -470,18 +471,18 @@
     {
       if (current.storageCompressionEnabled)
       {
-        OrthancPlugins::LogWarning("Housekeeper: storage compression is now enabled -> will perform housekeeping");
+        LOG(WARNING) << "Housekeeper: storage compression is now enabled -> will perform housekeeping";
       }
       else
       {
-        OrthancPlugins::LogWarning("Housekeeper: storage compression is now disabled -> will perform housekeeping");
+        LOG(WARNING) << "Housekeeper: storage compression is now disabled -> will perform housekeeping";
       }
       
       needsReingest = true;
     }
     else
     {
-      OrthancPlugins::LogWarning("Housekeeper: storage compression has changed but the trigger is disabled");
+      LOG(WARNING) << "Housekeeper: storage compression has changed but the trigger is disabled";
     }
   }
 
@@ -489,13 +490,13 @@
   {
     if (triggerOnIngestTranscodingChange_)
     {
-      OrthancPlugins::LogWarning("Housekeeper: ingest transcoding has changed -> will perform housekeeping");
+      LOG(WARNING) << "Housekeeper: ingest transcoding has changed -> will perform housekeeping";
       
       needsReingest = true;
     }
     else
     {
-      OrthancPlugins::LogWarning("Housekeeper: ingest transcoding has changed but the trigger is disabled");
+      LOG(WARNING) << "Housekeeper: ingest transcoding has changed but the trigger is disabled";
     }
   }
 
@@ -505,7 +506,7 @@
     {
       if (triggerOnDicomWebCacheChange_)
       {
-        OrthancPlugins::LogWarning("Housekeeper: DicomWEB plugin is enabled and the housekeeper has never run, you might miss series metadata cache -> will perform housekeeping");
+        LOG(WARNING) << "Housekeeper: DicomWEB plugin is enabled and the housekeeper has never run, you might miss series metadata cache -> will perform housekeeping";
       }
       needsDicomWebCaching = triggerOnDicomWebCacheChange_;
     }
@@ -517,12 +518,12 @@
       {
         if (triggerOnDicomWebCacheChange_)
         {
-          OrthancPlugins::LogWarning("Housekeeper: DicomWEB plugin might miss series metadata cache -> will perform housekeeping");
+          LOG(WARNING) << "Housekeeper: DicomWEB plugin might miss series metadata cache -> will perform housekeeping";
           needsDicomWebCaching = true;
         }
         else
         {
-          OrthancPlugins::LogWarning("Housekeeper: DicomWEB plugin might miss series metadata cache but the trigger has been disabled");
+          LOG(WARNING) << "Housekeeper: DicomWEB plugin might miss series metadata cache but the trigger has been disabled";
         }
       }
     }
@@ -659,7 +660,7 @@
 
   if (!needsProcessing)
   {
-    OrthancPlugins::LogWarning("Housekeeper: everything has been processed already !");
+    LOG(WARNING) << "Housekeeper: everything has been processed already !";
     return;
   }
 
@@ -667,11 +668,11 @@
   {
     if (force_)
     {
-      OrthancPlugins::LogWarning("Housekeeper: forcing execution -> will perform housekeeping");
+      LOG(WARNING) << "Housekeeper: forcing execution -> will perform housekeeping";
     }
     else
     {
-      OrthancPlugins::LogWarning("Housekeeper: the DB configuration has changed since last run, will reprocess the whole DB !");
+      LOG(WARNING) << "Housekeeper: the DB configuration has changed since last run, will reprocess the whole DB !";
     }
     
     Json::Value changes;
@@ -687,7 +688,7 @@
   }
   else
   {
-    OrthancPlugins::LogWarning("Housekeeper: the DB configuration has not changed since last run, will continue processing changes");
+    LOG(WARNING) << "Housekeeper: the DB configuration has not changed since last run, will continue processing changes";
   }
 
   bool completed = false;
@@ -709,9 +710,7 @@
       {
         boost::recursive_mutex::scoped_lock lock(pluginStatusMutex_);
     
-        OrthancPlugins::LogInfo("Housekeeper: processed changes " + 
-                                boost::lexical_cast<std::string>(pluginStatus_.lastProcessedChange) + 
-                                " / " + boost::lexical_cast<std::string>(pluginStatus_.lastChangeToProcess));
+        LOG(INFO) << "Housekeeper: processed changes " << pluginStatus_.lastProcessedChange << " / " << pluginStatus_.lastChangeToProcess;
         
         boost::this_thread::sleep(boost::posix_time::milliseconds(throttleDelay_ * 100));  // wait 1/10 of the delay between changes
       }
@@ -722,7 +721,7 @@
     {
       if (!loggedNotRightPeriodChangeMessage)
       {
-        OrthancPlugins::LogInfo("Housekeeper: entering quiet period");
+        LOG(INFO) << "Housekeeper: entering quiet period";
         loggedNotRightPeriodChangeMessage = true;
       }
 
@@ -807,7 +806,7 @@
       return -1;
     }
 
-    OrthancPlugins::LogWarning("Housekeeper plugin is initializing");
+    LOG(WARNING) << "Housekeeper plugin is initializing";
     OrthancPluginSetDescription2(c, HOUSEKEEPER_NAME, "Optimizes your DB and storage.");
 
     OrthancPlugins::OrthancConfiguration orthancConfiguration;
@@ -891,7 +890,7 @@
       if (limitMainDicomTagsReconstructLevel_ != "Patient" && limitMainDicomTagsReconstructLevel_ != "Study"
         && limitMainDicomTagsReconstructLevel_ != "Series" && limitMainDicomTagsReconstructLevel_ != "Instance")
       {
-        OrthancPlugins::LogError("Housekeeper invalid value for 'LimitMainDicomTagsReconstructLevel': '" + limitMainDicomTagsReconstructLevel_ + "'");
+        LOG(ERROR) << "Housekeeper invalid value for 'LimitMainDicomTagsReconstructLevel': '" << limitMainDicomTagsReconstructLevel_ << "'";
       }
       else if (limitMainDicomTagsReconstructLevel_ == "Patient")
       {
@@ -925,7 +924,7 @@
     }
     else
     {
-      OrthancPlugins::LogWarning("Housekeeper plugin is disabled by the configuration file");
+      LOG(WARNING) << "Housekeeper plugin is disabled by the configuration file";
     }
 
     return 0;
@@ -934,7 +933,7 @@
 
   ORTHANC_PLUGINS_API void OrthancPluginFinalize()
   {
-    OrthancPlugins::LogWarning("Housekeeper plugin is finalizing");
+    LOG(WARNING) << "Housekeeper plugin is finalizing";
   }
 
 
--- a/OrthancServer/Plugins/Samples/ModalityWorklists/Plugin.cpp	Mon Apr 22 10:50:33 2024 +0200
+++ b/OrthancServer/Plugins/Samples/ModalityWorklists/Plugin.cpp	Tue Apr 23 09:34:02 2024 +0200
@@ -23,6 +23,7 @@
 #define MODALITY_WORKLISTS_NAME "worklists"
 
 #include "../../../../OrthancFramework/Sources/Compatibility.h"
+#include "../../../../OrthancFramework/Sources/Logging.h"
 #include "../Common/OrthancPluginCppWrapper.h"
 
 #include <boost/filesystem.hpp>
@@ -54,7 +55,7 @@
 
     if (code != OrthancPluginErrorCode_Success)
     {
-      OrthancPlugins::LogError("Error while adding an answer to a worklist request");
+      LOG(ERROR) << "Error while adding an answer to a worklist request";
       ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code);
     }
 
@@ -77,8 +78,7 @@
   dicom.DicomToJson(json, OrthancPluginDicomToJsonFormat_Short,
                     static_cast<OrthancPluginDicomToJsonFlags>(0), 0);
 
-  OrthancPlugins::LogInfo("Received worklist query from remote modality " +
-                          std::string(issuerAet) + ":\n" + json.toStyledString());
+  LOG(INFO) << "Received worklist query from remote modality " << issuerAet << ":\n" + json.toStyledString();
 
   if (!filterIssuerAet_)
   {
@@ -185,21 +185,19 @@
                 return OrthancPluginErrorCode_Success;
               }
               
-              OrthancPlugins::LogInfo("Worklist matched: " + it->path().string());
+              LOG(INFO) << "Worklist matched: " << it->path().string();
               matchedWorklistCount++;
             }
           }
         }
       }
 
-      std::ostringstream message;
-      message << "Worklist C-Find: parsed " << parsedFilesCount
-              << " files, found " << matchedWorklistCount << " match(es)";
-      OrthancPlugins::LogInfo(message.str());
+      LOG(INFO) << "Worklist C-Find: parsed " << parsedFilesCount
+                << " files, found " << matchedWorklistCount << " match(es)";
     }
     catch (fs::filesystem_error&)
     {
-      OrthancPlugins::LogError("Inexistent folder while scanning for worklists: " + source.string());
+      LOG(ERROR) << "Inexistent folder while scanning for worklists: " << source.string();
       return OrthancPluginErrorCode_DirectoryExpected;
     }
 
@@ -227,7 +225,7 @@
       return -1;
     }
 
-    OrthancPlugins::LogWarning("Sample worklist plugin is initializing");
+    LOG(WARNING) << "Sample worklist plugin is initializing";
     OrthancPluginSetDescription2(c, MODALITY_WORKLISTS_NAME, "Serve DICOM modality worklists from a folder with Orthanc.");
 
     OrthancPlugins::OrthancConfiguration configuration;
@@ -240,12 +238,12 @@
     {
       if (worklists.LookupStringValue(folder_, "Database"))
       {
-        OrthancPlugins::LogWarning("The database of worklists will be read from folder: " + folder_);
+        LOG(WARNING) << "The database of worklists will be read from folder: " << folder_;
         OrthancPluginRegisterWorklistCallback(OrthancPlugins::GetGlobalContext(), Callback);
       }
       else
       {
-        OrthancPlugins::LogError("The configuration option \"Worklists.Database\" must contain a path");
+        LOG(ERROR) << "The configuration option \"Worklists.Database\" must contain a path";
         return -1;
       }
 
@@ -254,7 +252,7 @@
     }
     else
     {
-      OrthancPlugins::LogWarning("Worklist server is disabled by the configuration file");
+      LOG(WARNING) << "Worklist server is disabled by the configuration file";
     }
 
     return 0;
@@ -263,7 +261,7 @@
 
   ORTHANC_PLUGINS_API void OrthancPluginFinalize()
   {
-    OrthancPlugins::LogWarning("Sample worklist plugin is finalizing");
+    LOG(WARNING) << "Sample worklist plugin is finalizing";
   }
 
 
--- a/OrthancServer/Plugins/Samples/MultitenantDicom/Plugin.cpp	Mon Apr 22 10:50:33 2024 +0200
+++ b/OrthancServer/Plugins/Samples/MultitenantDicom/Plugin.cpp	Tue Apr 23 09:34:02 2024 +0200
@@ -120,7 +120,7 @@
 {
   ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context)
   {
-    OrthancPlugins::SetGlobalContext(context);
+    OrthancPlugins::SetGlobalContext(context, ORTHANC_PLUGIN_NAME);
 
     /* Check the version of the Orthanc core */
     if (OrthancPluginCheckVersion(OrthancPlugins::GetGlobalContext()) == 0)
@@ -141,9 +141,9 @@
     Orthanc::Logging::Initialize(context);
 #endif
 
-    if (!OrthancPlugins::CheckMinimalOrthancVersion(1, 12, 0))
+    if (!OrthancPlugins::CheckMinimalOrthancVersion(1, 12, 4))
     {
-      OrthancPlugins::ReportMinimalOrthancVersion(1, 12, 0);
+      OrthancPlugins::ReportMinimalOrthancVersion(1, 12, 4);
       return -1;
     }
 
--- a/OrthancServer/Plugins/Samples/ServeFolders/Plugin.cpp	Mon Apr 22 10:50:33 2024 +0200
+++ b/OrthancServer/Plugins/Samples/ServeFolders/Plugin.cpp	Tue Apr 23 09:34:02 2024 +0200
@@ -23,6 +23,7 @@
 #define SERVE_FOLDERS_NAME "serve-folders"
 
 #include "../Common/OrthancPluginCppWrapper.h"
+#include "../../../OrthancFramework/Sources/Logging.h"
 
 #include <json/value.h>
 #include <boost/filesystem.hpp>
@@ -94,7 +95,7 @@
   }
   else
   {
-    OrthancPlugins::LogWarning("ServeFolders: Unknown MIME type for extension \"" + extension + "\"");
+    LOG(WARNING) << "ServeFolders: Unknown MIME type for extension \"" << extension << "\"";
     return "application/octet-stream";
   }
 }
@@ -109,7 +110,7 @@
   std::map<std::string, std::string>::const_iterator found = folders_.find(uri);
   if (found == folders_.end())
   {
-    OrthancPlugins::LogError("Unknown URI in plugin server-folders: " + uri);
+    LOG(ERROR) << "Unknown URI in plugin server-folders: " << uri;
     OrthancPluginSendHttpStatusCode(OrthancPlugins::GetGlobalContext(), output, 404);
     return false;
   }
@@ -265,7 +266,7 @@
 {
   if (folders.type() != Json::objectValue)
   {
-    OrthancPlugins::LogError("The list of folders to be served is badly formatted (must be a JSON object)");
+    LOG(ERROR) << "The list of folders to be served is badly formatted (must be a JSON object)";
     ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
   }
 
@@ -277,8 +278,8 @@
   {
     if (folders[*it].type() != Json::stringValue)
     {
-      OrthancPlugins::LogError("The folder to be served \"" + *it + 
-                               "\" must be associated with a string value (its mapped URI)");
+      LOG(ERROR) << "The folder to be served \"" << *it << 
+                    "\" must be associated with a string value (its mapped URI)";
       ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
     }
 
@@ -299,7 +300,7 @@
 
     if (baseUri.empty())
     {
-      OrthancPlugins::LogError("The URI of a folder to be served cannot be empty");
+      LOG(ERROR) << "The URI of a folder to be served cannot be empty";
       ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
     }
 
@@ -307,7 +308,7 @@
     const std::string folder = folders[*it].asString();
     if (!boost::filesystem::is_directory(folder))
     {
-      OrthancPlugins::LogError("Trying to serve an inexistent folder: " + folder);
+      LOG(ERROR) << "Trying to serve an inexistent folder: " + folder;
       ORTHANC_PLUGINS_THROW_EXCEPTION(InexistentFile);
     }
 
@@ -326,7 +327,7 @@
 {
   if (extensions.type() != Json::objectValue)
   {
-    OrthancPlugins::LogError("The list of extensions is badly formatted (must be a JSON object)");
+    LOG(ERROR) << "The list of extensions is badly formatted (must be a JSON object)";
     ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
   }
 
@@ -337,8 +338,8 @@
   {
     if (extensions[*it].type() != Json::stringValue)
     {
-      OrthancPlugins::LogError("The file extension \"" + *it + 
-                               "\" must be associated with a string value (its MIME type)");
+      LOG(ERROR) << "The file extension \"" << *it << 
+                    "\" must be associated with a string value (its MIME type)";
       ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat);
     }
 
@@ -356,13 +357,11 @@
 
     if (mime.empty())
     {
-      OrthancPlugins::LogWarning("ServeFolders: Removing MIME type for file extension \"." +
-                                 name + "\"");
+      LOG(WARNING) << "ServeFolders: Removing MIME type for file extension \"." << name << "\"";
     }
     else
     {
-      OrthancPlugins::LogWarning("ServeFolders: Associating file extension \"." + name + 
-                                 "\" with MIME type \"" + mime + "\"");
+      LOG(WARNING) << "ServeFolders: Associating file extension \"." << name << "\" with MIME type \"" << mime << "\"";
     }
   }  
 }
@@ -392,17 +391,13 @@
     if (configuration.LookupBooleanValue(tmp, "AllowCache"))
     {
       allowCache_ = tmp;
-      OrthancPlugins::LogWarning("ServeFolders: Requesting the HTTP client to " +
-                                 std::string(tmp ? "enable" : "disable") + 
-                                 " its caching mechanism");
+      LOG(WARNING) << "ServeFolders: Requesting the HTTP client to " << (tmp ? "enable" : "disable") << " its caching mechanism";
     }
 
     if (configuration.LookupBooleanValue(tmp, "GenerateETag"))
     {
       generateETag_ = tmp;
-      OrthancPlugins::LogWarning("ServeFolders: The computation of an ETag for the "
-                                 "served resources is " +
-                                 std::string(tmp ? "enabled" : "disabled"));
+      LOG(WARNING) << "ServeFolders: The computation of an ETag for the served resources is " << (tmp ? "enabled" : "disabled");
     }
 
     OrthancPlugins::OrthancConfiguration extensions;
@@ -412,8 +407,7 @@
 
   if (folders_.empty())
   {
-    OrthancPlugins::LogWarning("ServeFolders: Empty configuration file: "
-                               "No additional folder will be served!");
+    LOG(WARNING) << "ServeFolders: Empty configuration file: No additional folder will be served!";
   }
 }
 
@@ -444,8 +438,7 @@
     }
     catch (OrthancPlugins::PluginException& e)
     {
-      OrthancPlugins::LogError("Error while initializing the ServeFolders plugin: " + 
-                               std::string(e.What(context)));
+      LOG(ERROR) << "Error while initializing the ServeFolders plugin: " << e.What(context);
     }
 
     return 0;
--- a/TODO	Mon Apr 22 10:50:33 2024 +0200
+++ b/TODO	Tue Apr 23 09:34:02 2024 +0200
@@ -56,10 +56,6 @@
   - ...
 * Investigate if one could fix KeepAlive race conditions:
   https://discourse.orthanc-server.org/t/socket-hangup-with-rest-api/4023/3
-* Logging: PHI can appear in logs when failing to write an archive:
-  E0418 14:05:41.769994 OrthancException.cpp:61] Error in the network protocol: HTTP client has disconnected while creating an archive in synchronous mode
-  E0418 14:05:41.769994 OrthancException.cpp:61] Cannot write to file: Cannot add new file inside ZIP archive: 13...63 S......RI 40Y M/DR D B...I L SPINE TBI/MR t1_fse_sag/MR000026.dcm
-  E0418 14:05:41.769994 ArchiveJob.cpp:1342] Error while creating an archive: Cannot write to file
 * The DICOM file cache shall keep a MD5 of the cached file and compare it with MD5
   from the DB.  That would allow 2 orthancs in a swarm to realize when the other
   Orthanc has updated the file:
@@ -269,8 +265,6 @@
 * Provide a C++ callback similar to "ReceivedInstanceFilter()" in Lua
   https://orthanc.uclouvain.be/book/users/lua.html#filtering-incoming-dicom-instances
   https://groups.google.com/d/msg/orthanc-users/BtvLTE5Ni8A/vIMhmMgfBAAJ
-* In "OrthancPluginLog[Error|Warning|Info]()", prefix the log line with
-  the name of the plugin, as retrieved by "OrthancPluginGetName()"
 * Update the SDK to handle buffer sizes > 4GB (all sizes are currently coded in uint32_t)
 * Add a C-Get SCP handler: OrthancPluginRegisterGetCallback
   https://groups.google.com/g/orthanc-users/c/NRhPkYX9IXQ/m/mWS11g0jBwAJ