changeset 7162:e72e3adae0cc

warnings can now be disabled directly in the Logging classes making it available to the OrthancFramework as well + added W101_TruncatingDicomTagTo256Values
author Alain Mazy <am@orthanc.team>
date Tue, 29 Sep 2026 16:25:10 +0200
parents f696c8086d5e
children 5e8003d46cf5
files NEWS OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp OrthancFramework/Sources/Logging.cpp OrthancFramework/Sources/Logging.h OrthancServer/Plugins/Engine/PluginsManager.cpp OrthancServer/Resources/AdvancedConfiguration.json OrthancServer/Sources/OrthancConfiguration.cpp OrthancServer/Sources/OrthancConfiguration.h OrthancServer/Sources/ResourceFinder.cpp OrthancServer/Sources/ResourceFinder.h OrthancServer/Sources/ServerContext.cpp OrthancServer/Sources/ServerEnumerations.h OrthancServer/Sources/main.cpp
diffstat 13 files changed, 105 insertions(+), 137 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Mon Sep 28 16:20:59 2026 +0200
+++ b/NEWS	Tue Sep 29 16:25:10 2026 +0200
@@ -30,6 +30,8 @@
 * Added more tolerance to invalid OW value representations
 * Fixed handling of "DicomModalities.LocalAet" handling in /modalities/../find-worklists
   (https://discourse.orthanc-server.org/t/find-worklist-with-localaet-from-modality-configuration/6482)
+* Added new warnings that can be disabled in the configuration: 
+  - W101_TruncatingDicomTagTo256Values
 * Fix use of the static runtime under Visual Studio (/MT), which broke in 1.12.10
 * New CMake options: 
   - "THIRD_PARTY_DOWNLOADS_ROOT_URL" to specify another webserver to downloads the third-parties from.
--- a/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Mon Sep 28 16:20:59 2026 +0200
+++ b/OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp	Tue Sep 29 16:25:10 2026 +0200
@@ -677,7 +677,7 @@
              **/
 
             DicomTag t(tag.getGroup(), tag.getElement());
-            LOG(WARNING) << "Truncating the DICOM tag " << t.Format() << " containing "
+            LOG_WARNING("W101") << "Truncating the DICOM tag " << t.Format() << " containing "
                          << size << " values to " << maxBinaryArrayLength << " values";
             size = maxBinaryArrayLength;
           }
--- a/OrthancFramework/Sources/Logging.cpp	Mon Sep 28 16:20:59 2026 +0200
+++ b/OrthancFramework/Sources/Logging.cpp	Tue Sep 29 16:25:10 2026 +0200
@@ -30,6 +30,9 @@
 #include <cassert>
 #include <stdint.h>
 #include <string.h>
+#include <boost/thread/shared_mutex.hpp>
+#include <boost/thread/lock_types.hpp>  // For boost::unique_lock<> and boost::shared_lock<>
+
 
 #if defined(__linux__) && !defined(NDEBUG)
 #  include <pthread.h>
@@ -52,6 +55,8 @@
     static uint32_t traceCategoriesMask_ = 0;
     static std::string logTargetFolder_;            // keep a track of the log folder in case of reset of the context
     static std::string logTargetFile_;              // keep a track of the log file in case of reset of the context
+    static boost::shared_mutex messageIdsMutex_;
+    static std::set<std::string> disabledMessageIds_;
 
     const char* EnumerationToString(LogLevel level)
     {
@@ -141,6 +146,28 @@
       return (traceCategoriesMask_ != 0);
     }
 
+    void SetMessageIdEnabled(const std::string& messageId, 
+                             bool enabled)
+    {
+      boost::unique_lock<boost::shared_mutex> lock(messageIdsMutex_);
+
+      if (enabled)
+      {
+        disabledMessageIds_.erase(messageId);
+      }
+      else
+      {
+        disabledMessageIds_.insert(messageId);
+      }
+    }
+
+    bool IsMessageIdEnabled(const std::string& messageId)
+    {
+      boost::shared_lock<boost::shared_mutex> lock(messageIdsMutex_);
+
+      return disabledMessageIds_.find(messageId) == disabledMessageIds_.end();
+    }
+
 
     void SetCategoryEnabled(LogLevel level,
                             LogCategory category,
@@ -953,7 +980,6 @@
 static std::list<Orthanc::Logging::ILoggingListener*> loggingListeners_;
 static boost::shared_mutex                            loggingListenersMutex_;
 
-
 namespace Orthanc
 {
   namespace Logging
@@ -1413,13 +1439,15 @@
                                    LogCategory category,
                                    const char* pluginName,
                                    const char* file,
-                                   int line) :
+                                   int line,
+                                   const char* messageId) :
       pimpl_(new PImpl),
       level_(level),
       stream_(&nullStream_),  // By default, logging to "/dev/null" is simulated
       category_(category),
       file_(file),
-      line_(line)
+      line_(line),
+      messageId_(messageId)
     {
       if (pluginContext_ != NULL)
       {
@@ -1447,9 +1475,9 @@
       {
         // We are logging in a standalone application, not inside an Orthanc plugin
 
-        if (!IsCategoryEnabled(level_, category))
+        if (!IsCategoryEnabled(level_, category) || (messageId_ != NULL && !IsMessageIdEnabled(messageId_)))
         {
-          // This logging level is disabled, directly exit as the
+          // This logging level or specific message is disabled, directly exit as the
           // stream is set to "/dev/null"
           return;
         }
@@ -1502,6 +1530,11 @@
             try
             {
               (*stream_) << prefix;
+
+              if (messageId_ != NULL)
+              {
+                (*stream_) << messageId_ << ": ";
+              }
             }
             catch (...)
             {
@@ -1573,7 +1606,7 @@
           {
             try
             {
-              (*it)->HandleLog(level_, category_, pluginName_, file_, line_, messageStream_.str());
+              (*it)->HandleLog(level_, category_, pluginName_, file_, line_, messageStream_.str(), messageId_);
             }
             catch (...) // NOLINT(bugprone-empty-catch)
             {
--- a/OrthancFramework/Sources/Logging.h	Mon Sep 28 16:20:59 2026 +0200
+++ b/OrthancFramework/Sources/Logging.h	Tue Sep 29 16:25:10 2026 +0200
@@ -113,7 +113,8 @@
                              const std::string& pluginName,
                              const char* file,
                              uint32_t line,
-                             const std::string& message) = 0;
+                             const std::string& message,
+                             const char* messageId) = 0;
     };
 
 
@@ -173,6 +174,11 @@
     ORTHANC_PUBLIC bool LookupCategory(LogCategory& target,
                                        const std::string& category);
 
+    ORTHANC_PUBLIC void SetMessageIdEnabled(const std::string& messageId, 
+                                            bool enabled);
+
+    ORTHANC_PUBLIC bool IsMessageIdEnabled(const std::string& messageId);
+
     ORTHANC_PUBLIC unsigned int GetCategoriesCount();
 
     ORTHANC_PUBLIC const char* GetCategoryName(unsigned int i);
@@ -247,9 +253,10 @@
 
 #if ORTHANC_ENABLE_LOGGING != 1
 #  define LOG(level)            ::Orthanc::Logging::NullStream()
+#  define LOG_WARNING(warningId) ::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()
+#  define LOG_FROM_PLUGIN(level, category, pluginName, file, line, messageId)  ::Orthanc::Logging::NullStream()
 #else /* ORTHANC_ENABLE_LOGGING == 1 */
 
 #if !defined(__ORTHANC_FILE__)
@@ -264,20 +271,25 @@
 #  define LOG(level)     ::Orthanc::Logging::InternalLogger             \
   (::Orthanc::Logging::LogLevel_ ## level,                              \
    ::Orthanc::Logging::LogCategory_GENERIC, NULL /* no plugin */,       \
-   __ORTHANC_FILE__, __LINE__)
+   __ORTHANC_FILE__, __LINE__, NULL)
+
+#  define LOG_WARNING(warningId)     ::Orthanc::Logging::InternalLogger             \
+  (::Orthanc::Logging::LogLevel_WARNING,                              \
+   ::Orthanc::Logging::LogCategory_GENERIC, NULL /* no plugin */,       \
+   __ORTHANC_FILE__, __LINE__, warningId)
 
 #  define VLOG(unused)   ::Orthanc::Logging::InternalLogger             \
   (::Orthanc::Logging::LogLevel_TRACE,                                  \
    ::Orthanc::Logging::LogCategory_GENERIC, NULL /* no plugin */,       \
-   __ORTHANC_FILE__, __LINE__)
+   __ORTHANC_FILE__, __LINE__, NULL)
 
 #  define CLOG(level, category) ::Orthanc::Logging::InternalLogger      \
   (::Orthanc::Logging::LogLevel_ ## level,                              \
    ::Orthanc::Logging::LogCategory_ ## category, NULL /* no plugin */,  \
-   __ORTHANC_FILE__, __LINE__)
+   __ORTHANC_FILE__, __LINE__, NULL)
 
-#  define LOG_FROM_PLUGIN(level, category, pluginName, file, line)      \
-  ::Orthanc::Logging::InternalLogger(level, category, pluginName, file, line)
+#  define LOG_FROM_PLUGIN(level, category, pluginName, file, line, messageId)      \
+  ::Orthanc::Logging::InternalLogger(level, category, pluginName, file, line, messageId)
 
 #endif
 
@@ -305,7 +317,8 @@
                      LogCategory category,
                      const char* pluginName /* ignored */,
                      const char* file  /* ignored */,
-                     int line  /* ignored */) :
+                     int line  /* ignored */,
+                     const char* messageId /* ignored */) :
         level_(level),
         category_(category)
       {
@@ -346,13 +359,15 @@
       const char*                         file_;
       uint32_t                            line_;
       std::stringstream                   messageStream_;
+      const char*                         messageId_;
 
     public:
       InternalLogger(LogLevel level,
                      LogCategory category,
                      const char* pluginName,
                      const char* file,
-                     int line);
+                     int line,
+                     const char* messageId);
 
       ~InternalLogger();
 
--- a/OrthancServer/Plugins/Engine/PluginsManager.cpp	Mon Sep 28 16:20:59 2026 +0200
+++ b/OrthancServer/Plugins/Engine/PluginsManager.cpp	Tue Sep 29 16:25:10 2026 +0200
@@ -171,7 +171,7 @@
         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;
+        LOG_FROM_PLUGIN(level, category, m.plugin, m.file, m.line, NULL) << m.message;
         return OrthancPluginErrorCode_Success;
       }
 
--- a/OrthancServer/Resources/AdvancedConfiguration.json	Mon Sep 28 16:20:59 2026 +0200
+++ b/OrthancServer/Resources/AdvancedConfiguration.json	Tue Sep 29 16:25:10 2026 +0200
@@ -15,6 +15,8 @@
   // "true" enables a warning. All warnings are enabled by default
   // see https://orthanc.uclouvain.be/book/faq/main-dicom-tags.html#warnings
   // (new in Orthanc 1.11.0)
+  // Note, from Orthanc 1.13.1, only the WXXX prefix is relevant.  The text
+  // following the underscore is ignored. 
   "Warnings" : {
     // A "RequestedTags" has been read from storage which is slower than
     // reading it from DB.
@@ -55,7 +57,11 @@
     // Display a warning when a user requests a tag that can not be read from disk
     // because "StorageAccessOnFind" is set to "Never".
     // (new in Orthanc 1.12.5)
-    "W007_MissingRequestedTagsNotReadFromDisk" : true
+    "W007_MissingRequestedTagsNotReadFromDisk" : true,
+
+    // Display a warning when Orthanc is truncating DICOMTag whose multiplicity is
+    // higher than 256 values when converting the tags to JSON.
+    "W101_TruncatingDicomTagTo256Values" : true
   },
 
   // Path to the directory where Orthanc stores its large temporary
--- a/OrthancServer/Sources/OrthancConfiguration.cpp	Mon Sep 28 16:20:59 2026 +0200
+++ b/OrthancServer/Sources/OrthancConfiguration.cpp	Tue Sep 29 16:25:10 2026 +0200
@@ -1367,54 +1367,23 @@
 
       for (size_t i = 0; i < members.size(); i++)
       {
-        const std::string& name = members[i];
+        const std::string& name = members[i];  // name is something like W001_TagsBeingReadFromStorage or W123
         bool enabled = warnings[name].asBool();
 
-        Warnings warning = Warnings_None;
-        if (name == "W001_TagsBeingReadFromStorage")
-        {
-          warning = Warnings_001_TagsBeingReadFromStorage;
-        }
-        else if (name == "W002_InconsistentDicomTagsInDb")
-        {
-          warning = Warnings_002_InconsistentDicomTagsInDb;
-        }
-        else if (name == "W003_DecoderFailure")
-        {
-          warning = Warnings_003_DecoderFailure;
-        }
-        else if (name == "W004_NoMainDicomTagsSignature")
+        std::vector<std::string> splitName;
+        Toolbox::SplitString(splitName, name, '_');
+
+        if (splitName.size() >= 1)
         {
-          warning = Warnings_004_NoMainDicomTagsSignature;
-        }
-        else if (name == "W005_RequestingTagFromLowerResourceLevel")
-        {
-          warning = Warnings_005_RequestingTagFromLowerResourceLevel;
-        }
-        else if (name == "W006_RequestingTagFromMetaHeader")
-        {
-          warning = Warnings_006_RequestingTagFromMetaHeader;
-        }
-        else if (name == "W007_MissingRequestedTagsNotReadFromDisk")
-        {
-          warning = Warnings_007_MissingRequestedTagsNotReadFromDisk;
-        }
-        else
-        {
-          throw OrthancException(ErrorCode_BadFileFormat, name + " is not recognized as a valid warning name");
+          Logging::SetMessageIdEnabled(splitName[0], enabled);
+          if (!enabled)
+          {
+            LOG(WARNING) << "Disabling warning '" << splitName[0] << "'";
+          }
         }
 
-        if (!enabled)
-        {
-          disabledWarnings_.insert(warning);
-        }
       }
     }
-    else
-    {
-      disabledWarnings_.clear();
-    }
-
   }
 
 
--- a/OrthancServer/Sources/OrthancConfiguration.h	Mon Sep 28 16:20:59 2026 +0200
+++ b/OrthancServer/Sources/OrthancConfiguration.h	Tue Sep 29 16:25:10 2026 +0200
@@ -96,7 +96,6 @@
     Peers                               peers_;
     JobsEngineThreadsCount              jobsEngineThreadsCount_;
     ServerIndex*                        serverIndex_;
-    std::set<Warnings>                  disabledWarnings_;
 
     OrthancConfiguration();
 
@@ -297,11 +296,6 @@
 
     std::string GetDatabaseServerIdentifier() const;
 
-    bool IsWarningEnabled(Warnings warning) const
-    {
-      return disabledWarnings_.count(warning) == 0;
-    }
-
     std::string GetOrthancAET() const;
 
     std::string GetMaximumStorageMode() const
--- a/OrthancServer/Sources/ResourceFinder.cpp	Mon Sep 28 16:20:59 2026 +0200
+++ b/OrthancServer/Sources/ResourceFinder.cpp	Tue Sep 29 16:25:10 2026 +0200
@@ -565,18 +565,8 @@
     limitsCount_(0),
     responseContent_(responseContent),
     storageAccessMode_(storageAccessMode),
-    supportsChildExistQueries_(supportsChildExistQueries),
-    isWarning002Enabled_(false),
-    isWarning004Enabled_(false),
-    isWarning005Enabled_(false)
+    supportsChildExistQueries_(supportsChildExistQueries)
   {
-    {
-      OrthancConfiguration::ReaderLock lock;
-      isWarning002Enabled_ = lock.GetConfiguration().IsWarningEnabled(Warnings_002_InconsistentDicomTagsInDb);
-      isWarning004Enabled_ = lock.GetConfiguration().IsWarningEnabled(Warnings_004_NoMainDicomTagsSignature);
-      isWarning005Enabled_ = lock.GetConfiguration().IsWarningEnabled(Warnings_005_RequestingTagFromLowerResourceLevel);
-    }
-
     request_.SetRetrieveMainDicomTags(responseContent_ & ResponseContentFlags_MainDicomTags);
     request_.SetRetrieveMetadata((responseContent_ & ResponseContentFlags_Metadata) || (responseContent_ & ResponseContentFlags_MetadataLegacy));
     request_.SetRetrieveLabels(responseContent_ & ResponseContentFlags_Labels);
@@ -740,11 +730,9 @@
     {
       if (request_.GetLevel() == ResourceType_Patient)
       {
-        if (isWarning005Enabled_)
-        {
-          LOG(WARNING) << "W005: Requested tag " << tag.Format()
-                       << " should only be read at the study, series, or instance level";
-        }
+        LOG_WARNING("W005") << "Requested tag " << tag.Format()
+                    << " should only be read at the study, series, or instance level";
+
         request_.SetRetrieveOneInstanceMetadataAndAttachments(true); // we might need to get it from one instance
       }
       else
@@ -765,11 +753,9 @@
       if (request_.GetLevel() == ResourceType_Patient ||
           request_.GetLevel() == ResourceType_Study)
       {
-        if (isWarning005Enabled_)
-        {
-          LOG(WARNING) << "W005: Requested tag " << tag.Format()
-                      << " should only be read at the series or instance level";
-        }
+        LOG_WARNING("W005") << "Requested tag " << tag.Format()
+                    << " should only be read at the series or instance level";
+
         request_.SetRetrieveOneInstanceMetadataAndAttachments(true); // we might need to get it from one instance
       }
       else
@@ -791,11 +777,9 @@
           request_.GetLevel() == ResourceType_Study ||
           request_.GetLevel() == ResourceType_Series)
       {
-        if (isWarning005Enabled_)
-        {
-          LOG(WARNING) << "W005: Requested tag " << tag.Format()
-                       << " should only be read at the instance level";
-        }
+        LOG_WARNING("W005") << "Requested tag " << tag.Format()
+                    << " should only be read at the instance level";
+
         request_.SetRetrieveOneInstanceMetadataAndAttachments(true); // we might need to get it from one instance
       }
       else
@@ -941,13 +925,11 @@
                                              const FindResponse::Resource& resource,
                                              const std::set<DicomTag>& missingTags)
   {
-    OrthancConfiguration::ReaderLock lock;
-    if (lock.GetConfiguration().IsWarningEnabled(Warnings_001_TagsBeingReadFromStorage))
     {
       std::string missings;
       FromDcmtkBridge::FormatListOfTags(missings, missingTags);
 
-      LOG(WARNING) << "W001: Accessing DICOM tags from storage when accessing "
+      LOG_WARNING("W001") << "Accessing DICOM tags from storage when accessing "
                    << Orthanc::GetResourceTypeText(resource.GetLevel(), false, false)
                    << " " << resource.GetIdentifier()
                    << ": " << missings;
@@ -1065,19 +1047,6 @@
                              "Unable to use 'Since' when finding resources when querying against Dicom Tags that are not in the MainDicomTags or when using CaseSenstive queries.");
     }
 
-    bool isWarning002Enabled = false;
-    bool isWarning004Enabled = false;
-    bool isWarning006Enabled = false;
-    bool isWarning007Enabled = false;
-
-    {
-      OrthancConfiguration::ReaderLock lock;
-      isWarning002Enabled = lock.GetConfiguration().IsWarningEnabled(Warnings_002_InconsistentDicomTagsInDb);
-      isWarning004Enabled = lock.GetConfiguration().IsWarningEnabled(Warnings_004_NoMainDicomTagsSignature);
-      isWarning006Enabled = lock.GetConfiguration().IsWarningEnabled(Warnings_006_RequestingTagFromMetaHeader);
-      isWarning007Enabled = lock.GetConfiguration().IsWarningEnabled(Warnings_007_MissingRequestedTagsNotReadFromDisk);
-    }
-
     FindResponse response;
     context.GetIndex().ExecuteFind(response, request_);
 
@@ -1144,11 +1113,10 @@
             }
           }
 
-          if (isWarning006Enabled)
           {
             std::string joinedMetaTags;
             FromDcmtkBridge::FormatListOfTags(joinedMetaTags, metaTagsToRemove);
-            LOG(WARNING) << "W006: Unable to include tags from the Meta Header in \"RequestedTags\".  Skipping them: " << joinedMetaTags;
+            LOG_WARNING("W006") << "Unable to include tags from the Meta Header in \"RequestedTags\".  Skipping them: " << joinedMetaTags;
           }
 
           Toolbox::RemoveSets(remainingRequestedTags, metaTagsToRemove);
@@ -1173,30 +1141,30 @@
           {
             ReadMissingTagsFromStorageArea(outRequestedTags, context, request_, resource, remainingRequestedTags);
           }
-          else if (isWarning007Enabled)
+          else
           {
             std::string joinedTags;
             FromDcmtkBridge::FormatListOfTags(joinedTags, remainingRequestedTags);
-            LOG(WARNING) << "W007: Unable to include requested tags since \"StorageAccessOnFind\" does not allow accessing the storage to build answers: " << joinedTags;
+            LOG_WARNING("W007") << "Unable to include requested tags since \"StorageAccessOnFind\" does not allow accessing the storage to build answers: " << joinedTags;
           }
         }
 
         std::string mainDicomTagsSignature;
-        if (isWarning002Enabled &&
+        if (Logging::IsMessageIdEnabled("W002") &&
             resource.LookupMetadata(mainDicomTagsSignature, resource.GetLevel(), MetadataType_MainDicomTagsSignature) &&
             mainDicomTagsSignature != DicomMap::GetMainDicomTagsSignature(resource.GetLevel()))
         {
-          LOG(WARNING) << "W002: " << Orthanc::GetResourceTypeText(resource.GetLevel(), false , false)
+          LOG_WARNING("W002") << Orthanc::GetResourceTypeText(resource.GetLevel(), false , false)
                        << " has been stored with another version of Main Dicom Tags list, you should POST to /"
                        << Orthanc::GetResourceTypeText(resource.GetLevel(), true, false)
                        << "/" << resource.GetIdentifier()
                        << "/reconstruct to update the list of tags saved in DB or run the Housekeeper plugin.  Some MainDicomTags might be missing from this answer.";
         }
-        else if (isWarning004Enabled &&
+        else if (Logging::IsMessageIdEnabled("W004") &&
                  request_.IsRetrieveMetadata() &&
                  !resource.LookupMetadata(mainDicomTagsSignature, resource.GetLevel(), MetadataType_MainDicomTagsSignature))
         {
-          LOG(WARNING) << "W004: " << Orthanc::GetResourceTypeText(resource.GetLevel(), false , false)
+          LOG_WARNING("W004") << Orthanc::GetResourceTypeText(resource.GetLevel(), false , false)
                        << " has been stored with an old Orthanc version and does not have a MainDicomTagsSignature, you should POST to /"
                        << Orthanc::GetResourceTypeText(resource.GetLevel(), true, false)
                        << "/" << resource.GetIdentifier()
--- a/OrthancServer/Sources/ResourceFinder.h	Mon Sep 28 16:20:59 2026 +0200
+++ b/OrthancServer/Sources/ResourceFinder.h	Tue Sep 29 16:25:10 2026 +0200
@@ -72,10 +72,6 @@
     std::set<DicomTag>               requestedTags_;
     std::set<DicomTag>               requestedComputedTags_;
 
-    bool                             isWarning002Enabled_;
-    bool                             isWarning004Enabled_;
-    bool                             isWarning005Enabled_;
-
     bool IsRequestedComputedTag(const DicomTag& tag) const
     {
       return requestedComputedTags_.find(tag) != requestedComputedTags_.end();
--- a/OrthancServer/Sources/ServerContext.cpp	Mon Sep 28 16:20:59 2026 +0200
+++ b/OrthancServer/Sources/ServerContext.cpp	Tue Sep 29 16:25:10 2026 +0200
@@ -2211,11 +2211,7 @@
 
     if (decoded.get() == NULL)
     {
-      OrthancConfiguration::ReaderLock configLock;
-      if (configLock.GetConfiguration().IsWarningEnabled(Warnings_003_DecoderFailure))
-      {
-        LOG(WARNING) << "W003: Unable to decode frame " << frameIndex << " from instance " << instancePublicId;
-      }
+      LOG_WARNING("W003") << "Unable to decode frame " << frameIndex << " from instance " << instancePublicId;
 
       throw OrthancException(ErrorCode_NotImplemented);
     }
--- a/OrthancServer/Sources/ServerEnumerations.h	Mon Sep 28 16:20:59 2026 +0200
+++ b/OrthancServer/Sources/ServerEnumerations.h	Tue Sep 29 16:25:10 2026 +0200
@@ -252,18 +252,6 @@
     BuiltinDecoderTranscoderOrder_Disabled
   };
 
-  enum Warnings
-  {
-    Warnings_None,
-    Warnings_001_TagsBeingReadFromStorage,
-    Warnings_002_InconsistentDicomTagsInDb,
-    Warnings_003_DecoderFailure,                           // new in Orthanc 1.12.5
-    Warnings_004_NoMainDicomTagsSignature,                 // new in Orthanc 1.12.5
-    Warnings_005_RequestingTagFromLowerResourceLevel,      // new in Orthanc 1.12.5
-    Warnings_006_RequestingTagFromMetaHeader,              // new in Orthanc 1.12.5
-    Warnings_007_MissingRequestedTagsNotReadFromDisk       // new in Orthanc 1.12.5
-  };
-
   enum QueueOrigin
   {
     QueueOrigin_Front,
--- a/OrthancServer/Sources/main.cpp	Mon Sep 28 16:20:59 2026 +0200
+++ b/OrthancServer/Sources/main.cpp	Tue Sep 29 16:25:10 2026 +0200
@@ -1112,7 +1112,8 @@
                          const std::string& pluginName,
                          const char* file,
                          uint32_t line,
-                         const std::string& message) ORTHANC_OVERRIDE
+                         const std::string& message,
+                         const char* messageId) ORTHANC_OVERRIDE
   {
     if (level == Orthanc::Logging::LogLevel_ERROR)
     {