changeset 3175:574890d14c92

new metrics: orthanc_store_dicom_duration_ms, orthanc_storage_[create|read|remove]_duration_ms
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 29 Jan 2019 17:34:09 +0100
parents 8ea7c4546c3a
children 784bbb03fb54
files Core/FileStorage/StorageAccessor.cpp Core/FileStorage/StorageAccessor.h Core/MetricsRegistry.cpp OrthancServer/OrthancRestApi/OrthancRestApi.cpp OrthancServer/OrthancRestApi/OrthancRestApi.h OrthancServer/ServerContext.cpp
diffstat 6 files changed, 130 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/Core/FileStorage/StorageAccessor.cpp	Tue Jan 29 15:15:48 2019 +0100
+++ b/Core/FileStorage/StorageAccessor.cpp	Tue Jan 29 17:34:09 2019 +0100
@@ -35,16 +35,39 @@
 #include "StorageAccessor.h"
 
 #include "../Compression/ZlibCompressor.h"
+#include "../MetricsRegistry.h"
 #include "../OrthancException.h"
 #include "../Toolbox.h"
-#include "../Toolbox.h"
 
 #if ORTHANC_ENABLE_CIVETWEB == 1 || ORTHANC_ENABLE_MONGOOSE == 1
 #  include "../HttpServer/HttpStreamTranscoder.h"
 #endif
 
+
+static const std::string METRICS_CREATE = "orthanc_storage_create_duration_ms";
+static const std::string METRICS_READ = "orthanc_storage_read_duration_ms";
+static const std::string METRICS_REMOVE = "orthanc_storage_remove_duration_ms";
+
+
 namespace Orthanc
 {
+  class StorageAccessor::MetricsTimer : public boost::noncopyable
+  {
+  private:
+    std::auto_ptr<MetricsRegistry::Timer>  timer_;
+
+  public:
+    MetricsTimer(StorageAccessor& that,
+                 const std::string& name)
+    {
+      if (that.metrics_ != NULL)
+      {
+        timer_.reset(new MetricsRegistry::Timer(*that.metrics_, name));
+      }
+    }
+  };
+
+
   FileInfo StorageAccessor::Write(const void* data,
                                   size_t size,
                                   FileContentType type,
@@ -64,6 +87,8 @@
     {
       case CompressionType_None:
       {
+        MetricsTimer timer(*this, METRICS_CREATE);
+
         area_.Create(uuid, data, size, type);
         return FileInfo(uuid, type, size, md5);
       }
@@ -82,13 +107,17 @@
           Toolbox::ComputeMD5(compressedMD5, compressed);
         }
 
-        if (compressed.size() > 0)
         {
-          area_.Create(uuid, &compressed[0], compressed.size(), type);
-        }
-        else
-        {
-          area_.Create(uuid, NULL, 0, type);
+          MetricsTimer timer(*this, METRICS_CREATE);
+
+          if (compressed.size() > 0)
+          {
+            area_.Create(uuid, &compressed[0], compressed.size(), type);
+          }
+          else
+          {
+            area_.Create(uuid, NULL, 0, type);
+          }
         }
 
         return FileInfo(uuid, type, size, md5,
@@ -108,6 +137,7 @@
     {
       case CompressionType_None:
       {
+        MetricsTimer timer(*this, METRICS_READ);
         area_.Read(content, info.GetUuid(), info.GetContentType());
         break;
       }
@@ -117,7 +147,12 @@
         ZlibCompressor zlib;
 
         std::string compressed;
-        area_.Read(compressed, info.GetUuid(), info.GetContentType());
+
+        {
+          MetricsTimer timer(*this, METRICS_READ);
+          area_.Read(compressed, info.GetUuid(), info.GetContentType());
+        }
+
         IBufferCompressor::Uncompress(content, zlib, compressed);
         break;
       }
@@ -132,17 +167,19 @@
   }
 
 
-  void StorageAccessor::Read(Json::Value& content,
-                             const FileInfo& info)
+  void StorageAccessor::ReadRaw(std::string& content,
+                                const FileInfo& info)
   {
-    std::string s;
-    Read(s, info);
+    MetricsTimer timer(*this, METRICS_READ);
+    area_.Read(content, info.GetUuid(), info.GetContentType());
+  }
 
-    Json::Reader reader;
-    if (!reader.parse(s, content))
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
+
+  void StorageAccessor::Remove(const std::string& fileUuid,
+                               FileContentType type)
+  {
+    MetricsTimer timer(*this, METRICS_REMOVE);
+    area_.Remove(fileUuid, type);
   }
 
 
@@ -151,7 +188,11 @@
                                     const FileInfo& info,
                                     const std::string& mime)
   {
-    area_.Read(sender.GetBuffer(), info.GetUuid(), info.GetContentType());
+    {
+      MetricsTimer timer(*this, METRICS_READ);
+      area_.Read(sender.GetBuffer(), info.GetUuid(), info.GetContentType());
+    }
+
     sender.SetContentType(mime);
 
     const char* extension;
--- a/Core/FileStorage/StorageAccessor.h	Tue Jan 29 15:15:48 2019 +0100
+++ b/Core/FileStorage/StorageAccessor.h	Tue Jan 29 17:34:09 2019 +0100
@@ -61,14 +61,23 @@
 #include <string>
 #include <boost/noncopyable.hpp>
 #include <stdint.h>
-#include <json/value.h>
 
 namespace Orthanc
 {
+  class MetricsRegistry;
+
+  /**
+   * This class handles the compression/decompression of the raw files
+   * contained in the storage area, and monitors timing metrics (if
+   * enabled).
+   **/
   class StorageAccessor : boost::noncopyable
   {
   private:
-    IStorageArea&  area_;
+    class MetricsTimer;
+
+    IStorageArea&     area_;
+    MetricsRegistry*  metrics_;
 
 #if ORTHANC_ENABLE_CIVETWEB == 1 || ORTHANC_ENABLE_MONGOOSE == 1
     void SetupSender(BufferHttpSender& sender,
@@ -77,7 +86,16 @@
 #endif
 
   public:
-    StorageAccessor(IStorageArea& area) : area_(area)
+    StorageAccessor(IStorageArea& area) : 
+      area_(area),
+      metrics_(NULL)
+    {
+    }
+
+    StorageAccessor(IStorageArea& area,
+                    MetricsRegistry& metrics) : 
+      area_(area),
+      metrics_(&metrics)
     {
     }
 
@@ -99,12 +117,15 @@
     void Read(std::string& content,
               const FileInfo& info);
 
-    void Read(Json::Value& content,
-              const FileInfo& info);
+    void ReadRaw(std::string& content,
+                 const FileInfo& info);
+
+    void Remove(const std::string& fileUuid,
+                FileContentType type);
 
     void Remove(const FileInfo& info)
     {
-      area_.Remove(info.GetUuid(), info.GetContentType());
+      Remove(info.GetUuid(), info.GetContentType());
     }
 
 #if ORTHANC_ENABLE_CIVETWEB == 1 || ORTHANC_ENABLE_MONGOOSE == 1
--- a/Core/MetricsRegistry.cpp	Tue Jan 29 15:15:48 2019 +0100
+++ b/Core/MetricsRegistry.cpp	Tue Jan 29 17:34:09 2019 +0100
@@ -41,10 +41,9 @@
 {
   static const boost::posix_time::ptime GetNow()
   {
-    return boost::posix_time::second_clock::universal_time();
+    return boost::posix_time::microsec_clock::universal_time();
   }
 
-
   class MetricsRegistry::Item
   {
   private:
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Tue Jan 29 15:15:48 2019 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.cpp	Tue Jan 29 17:34:09 2019 +0100
@@ -35,6 +35,7 @@
 #include "OrthancRestApi.h"
 
 #include "../../Core/Logging.h"
+#include "../../Core/MetricsRegistry.h"
 #include "../../Core/SerializationToolbox.h"
 #include "../ServerContext.h"
 
@@ -156,6 +157,24 @@
   }
 
 
+  bool OrthancRestApi::Handle(HttpOutput& output,
+                              RequestOrigin origin,
+                              const char* remoteIp,
+                              const char* username,
+                              HttpMethod method,
+                              const UriComponents& uri,
+                              const Arguments& headers,
+                              const GetArguments& getArguments,
+                              const char* bodyData,
+                              size_t bodySize)
+  {
+    MetricsRegistry::Timer timer(context_.GetMetricsRegistry(), "orthanc_rest_api_duration_ms");
+
+    return RestApi::Handle(output, origin, remoteIp, username, method,
+                           uri, headers, getArguments, bodyData, bodySize);
+  }
+
+
   ServerContext& OrthancRestApi::GetContext(RestApiCall& call)
   {
     return GetApi(call).context_;
--- a/OrthancServer/OrthancRestApi/OrthancRestApi.h	Tue Jan 29 15:15:48 2019 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestApi.h	Tue Jan 29 17:34:09 2019 +0100
@@ -75,6 +75,17 @@
   public:
     OrthancRestApi(ServerContext& context);
 
+    virtual bool Handle(HttpOutput& output,
+                        RequestOrigin origin,
+                        const char* remoteIp,
+                        const char* username,
+                        HttpMethod method,
+                        const UriComponents& uri,
+                        const Arguments& headers,
+                        const GetArguments& getArguments,
+                        const char* bodyData,
+                        size_t bodySize) ORTHANC_OVERRIDE;
+
     const bool& LeaveBarrierFlag() const
     {
       return leaveBarrier_;
--- a/OrthancServer/ServerContext.cpp	Tue Jan 29 15:15:48 2019 +0100
+++ b/OrthancServer/ServerContext.cpp	Tue Jan 29 17:34:09 2019 +0100
@@ -322,7 +322,8 @@
   void ServerContext::RemoveFile(const std::string& fileUuid,
                                  FileContentType type)
   {
-    area_.Remove(fileUuid, type);
+    StorageAccessor accessor(area_, GetMetricsRegistry());
+    accessor.Remove(fileUuid, type);
   }
 
 
@@ -331,7 +332,8 @@
   {
     try
     {
-      StorageAccessor accessor(area_);
+      MetricsRegistry::Timer timer(GetMetricsRegistry(), "orthanc_store_dicom_duration_ms");
+      StorageAccessor accessor(area_, GetMetricsRegistry());
 
       resultPublicId = dicom.GetHasher().HashInstance();
 
@@ -472,7 +474,7 @@
       throw OrthancException(ErrorCode_UnknownResource);
     }
 
-    StorageAccessor accessor(area_);
+    StorageAccessor accessor(area_, GetMetricsRegistry());
     accessor.AnswerFile(output, attachment, GetFileContentMime(content));
   }
 
@@ -500,7 +502,7 @@
 
     std::string content;
 
-    StorageAccessor accessor(area_);
+    StorageAccessor accessor(area_, GetMetricsRegistry());
     accessor.Read(content, attachment);
 
     FileInfo modified = accessor.Write(content.empty() ? NULL : content.c_str(),
@@ -616,15 +618,18 @@
                              " of instance " + instancePublicId);
     }
 
+    assert(attachment.GetContentType() == content);
+
     if (uncompressIfNeeded)
     {
       ReadAttachment(result, attachment);
     }
     else
     {
-      // Do not interpret the content of the storage area, return the
+      // Do not uncompress the content of the storage area, return the
       // raw data
-      area_.Read(result, attachment.GetUuid(), content);
+      StorageAccessor accessor(area_, GetMetricsRegistry());
+      accessor.ReadRaw(result, attachment);
     }
   }
 
@@ -633,7 +638,7 @@
                                      const FileInfo& attachment)
   {
     // This will decompress the attachment
-    StorageAccessor accessor(area_);
+    StorageAccessor accessor(area_, GetMetricsRegistry());
     accessor.Read(result, attachment);
   }
 
@@ -683,7 +688,7 @@
     // TODO Should we use "gzip" instead?
     CompressionType compression = (compressionEnabled_ ? CompressionType_ZlibWithSize : CompressionType_None);
 
-    StorageAccessor accessor(area_);
+    StorageAccessor accessor(area_, GetMetricsRegistry());
     FileInfo attachment = accessor.Write(data, size, attachmentType, compression, storeMD5_);
 
     StoreStatus status = index_.AddAttachment(attachment, resourceId);