Mercurial > hg > orthanc
diff Core/MetricsRegistry.cpp @ 3174:8ea7c4546c3a
primitives to collect metrics in Orthanc
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 29 Jan 2019 15:15:48 +0100 |
parents | |
children | 574890d14c92 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/MetricsRegistry.cpp Tue Jan 29 15:15:48 2019 +0100 @@ -0,0 +1,321 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "PrecompiledHeaders.h" +#include "MetricsRegistry.h" + +#include "OrthancException.h" +#include "ChunkedBuffer.h" + +namespace Orthanc +{ + static const boost::posix_time::ptime GetNow() + { + return boost::posix_time::second_clock::universal_time(); + } + + + class MetricsRegistry::Item + { + private: + MetricsType type_; + boost::posix_time::ptime time_; + bool hasValue_; + float value_; + + void Touch(float value, + const boost::posix_time::ptime& now) + { + hasValue_ = true; + value_ = value; + time_ = now; + } + + void Touch(float value) + { + Touch(value, GetNow()); + } + + void UpdateMax(float value, + int duration) + { + if (hasValue_) + { + const boost::posix_time::ptime now = GetNow(); + + if (value > value_ || + (now - time_).total_seconds() > duration) + { + Touch(value, now); + } + } + else + { + Touch(value); + } + } + + void UpdateMin(float value, + int duration) + { + if (hasValue_) + { + const boost::posix_time::ptime now = GetNow(); + + if (value < value_ || + (now - time_).total_seconds() > duration) + { + Touch(value, now); + } + } + else + { + Touch(value); + } + } + + public: + Item(MetricsType type) : + type_(type), + hasValue_(false) + { + } + + MetricsType GetType() const + { + return type_; + } + + void Update(float value) + { + switch (type_) + { + case MetricsType_Default: + Touch(value); + break; + + case MetricsType_MaxOver10Seconds: + UpdateMax(value, 10); + break; + + case MetricsType_MaxOver1Minute: + UpdateMax(value, 60); + break; + + case MetricsType_MinOver10Seconds: + UpdateMin(value, 10); + break; + + case MetricsType_MinOver1Minute: + UpdateMin(value, 60); + break; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } + + bool HasValue() const + { + return hasValue_; + } + + const boost::posix_time::ptime& GetTime() const + { + if (hasValue_) + { + return time_; + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } + + float GetValue() const + { + if (hasValue_) + { + return value_; + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } + }; + + + MetricsRegistry::~MetricsRegistry() + { + for (Content::iterator it = content_.begin(); it != content_.end(); ++it) + { + assert(it->second != NULL); + delete it->second; + } + } + + + void MetricsRegistry::SetEnabled(bool enabled) + { + boost::mutex::scoped_lock lock(mutex_); + enabled_ = enabled; + } + + + void MetricsRegistry::Register(const std::string& name, + MetricsType type) + { + boost::mutex::scoped_lock lock(mutex_); + + Content::iterator found = content_.find(name); + + if (found == content_.end()) + { + content_[name] = new Item(type); + } + else + { + assert(found->second != NULL); + + // This metrics already exists: Only recreate it if there is a + // mismatch in the type of metrics + if (found->second->GetType() != type) + { + delete found->second; + found->second = new Item(type); + } + } + } + + + void MetricsRegistry::SetValueInternal(const std::string& name, + float value, + MetricsType type) + { + boost::mutex::scoped_lock lock(mutex_); + + Content::iterator found = content_.find(name); + + if (found == content_.end()) + { + std::auto_ptr<Item> item(new Item(type)); + item->Update(value); + content_[name] = item.release(); + } + else + { + assert(found->second != NULL); + found->second->Update(value); + } + } + + + MetricsType MetricsRegistry::GetMetricsType(const std::string& name) + { + boost::mutex::scoped_lock lock(mutex_); + + Content::const_iterator found = content_.find(name); + + if (found == content_.end()) + { + throw OrthancException(ErrorCode_InexistentItem); + } + else + { + assert(found->second != NULL); + return found->second->GetType(); + } + } + + + void MetricsRegistry::ExportPrometheusText(std::string& s) + { + // https://www.boost.org/doc/libs/1_69_0/doc/html/date_time/examples.html#date_time.examples.seconds_since_epoch + static const boost::posix_time::ptime EPOCH(boost::gregorian::date(1970, 1, 1)); + + boost::mutex::scoped_lock lock(mutex_); + + s.clear(); + + if (!enabled_) + { + return; + } + + ChunkedBuffer buffer; + + for (Content::const_iterator it = content_.begin(); + it != content_.end(); ++it) + { + assert(it->second != NULL); + + if (it->second->HasValue()) + { + boost::posix_time::time_duration diff = it->second->GetTime() - EPOCH; + + std::string line = (it->first + " " + + boost::lexical_cast<std::string>(it->second->GetValue()) + " " + + boost::lexical_cast<std::string>(diff.total_milliseconds()) + "\n"); + + buffer.AddChunk(line); + } + } + + buffer.Flatten(s); + } + + + void MetricsRegistry::Timer::Start() + { + if (registry_.IsEnabled()) + { + active_ = true; + start_ = GetNow(); + } + else + { + active_ = false; + } + } + + + MetricsRegistry::Timer::~Timer() + { + if (active_) + { + boost::posix_time::time_duration diff = GetNow() - start_; + registry_.SetValue(name_, diff.total_milliseconds(), type_); + } + } +}