From 3f09f7d2de3d81215d227659efb9dbf8d8d1aed8 Mon Sep 17 00:00:00 2001 From: Nick Draper <nick.draper@stfc.ac.uk> Date: Tue, 1 Dec 2015 17:10:20 +0000 Subject: [PATCH] checkpointing re #12041 --- Framework/API/CMakeLists.txt | 3 - Framework/API/inc/MantidAPI/Algorithm.h | 8 + Framework/API/src/Algorithm.cpp | 45 ++- Framework/API/src/UsageService.cpp | 227 ------------- Framework/Kernel/CMakeLists.txt | 5 +- .../Kernel/inc/MantidKernel/ConfigService.h | 7 + .../inc/MantidKernel/UsageReporter.h} | 80 +++-- Framework/Kernel/src/ConfigService.cpp | 58 +++- Framework/Kernel/src/UsageReporter.cpp | 314 ++++++++++++++++++ .../test/UsageReporterTest.h} | 4 +- 10 files changed, 459 insertions(+), 292 deletions(-) delete mode 100644 Framework/API/src/UsageService.cpp rename Framework/{API/inc/MantidAPI/UsageService.h => Kernel/inc/MantidKernel/UsageReporter.h} (68%) create mode 100644 Framework/Kernel/src/UsageReporter.cpp rename Framework/{API/test/UsageServiceTest.h => Kernel/test/UsageReporterTest.h} (88%) diff --git a/Framework/API/CMakeLists.txt b/Framework/API/CMakeLists.txt index 77386a63202..4abf2d8319c 100644 --- a/Framework/API/CMakeLists.txt +++ b/Framework/API/CMakeLists.txt @@ -127,7 +127,6 @@ set ( SRC_FILES src/TableRow.cpp src/TextAxis.cpp src/TransformScaleFactory.cpp - src/UsageService.cpp src/Workspace.cpp src/WorkspaceFactory.cpp src/WorkspaceGroup.cpp @@ -310,7 +309,6 @@ set ( INC_FILES inc/MantidAPI/TableRow.h inc/MantidAPI/TextAxis.h inc/MantidAPI/TransformScaleFactory.h - inc/MantidAPI/UsageService.h inc/MantidAPI/VectorParameter.h inc/MantidAPI/VectorParameterParser.h inc/MantidAPI/Workspace.h @@ -411,7 +409,6 @@ set ( TEST_FILES SpectraAxisValidatorTest.h SpectrumDetectorMappingTest.h TextAxisTest.h - UsageServiceTest.h VectorParameterParserTest.h VectorParameterTest.h WorkspaceFactoryTest.h diff --git a/Framework/API/inc/MantidAPI/Algorithm.h b/Framework/API/inc/MantidAPI/Algorithm.h index 10f721243d0..48ef1ef22b1 100644 --- a/Framework/API/inc/MantidAPI/Algorithm.h +++ b/Framework/API/inc/MantidAPI/Algorithm.h @@ -32,6 +32,10 @@ template <class C, class N> class NObserver; class Void; } +namespace Json { +class Value; +} + namespace Mantid { namespace API { //---------------------------------------------------------------------- @@ -273,6 +277,8 @@ public: //@{ /// Serialize an object to a string virtual std::string toString() const; + /// Serialize an object to a json object + ::Json::Value toJson() const; /// De-serialize an object from a string static IAlgorithm_sptr fromString(const std::string &input); /// Construct an object from a history entry @@ -400,6 +406,8 @@ private: void reportCompleted(const double &duration, const bool groupProcessing = false); + void registerFeatureUsage(const float &duration) const; + // --------------------- Private Members ----------------------------------- /// Poco::ActiveMethod used to implement asynchronous execution. Poco::ActiveMethod<bool, Poco::Void, Algorithm, diff --git a/Framework/API/src/Algorithm.cpp b/Framework/API/src/Algorithm.cpp index 7ce22488522..f874d0a0126 100644 --- a/Framework/API/src/Algorithm.cpp +++ b/Framework/API/src/Algorithm.cpp @@ -9,14 +9,15 @@ #include "MantidAPI/AlgorithmManager.h" #include "MantidAPI/MemoryManager.h" #include "MantidAPI/IWorkspaceProperty.h" -#include "MantidAPI/UsageService.h" #include "MantidAPI/WorkspaceGroup.h" +#include "MantidKernel/ConfigService.h" #include "MantidKernel/EmptyValues.h" #include "MantidKernel/DateAndTime.h" #include "MantidKernel/MultiThreaded.h" #include "MantidKernel/Strings.h" #include "MantidKernel/Timer.h" +#include "MantidKernel/UsageReporter.h" #include <boost/algorithm/string/regex.hpp> #include <boost/weak_ptr.hpp> @@ -604,7 +605,7 @@ bool Algorithm::execute() { if (!isChild() || m_alwaysStoreInADS) this->store(); - UsageService::Instance().registerFeatureUsage(this, duration); + registerFeatureUsage(duration); // RJT, 19/3/08: Moved this up from below the catch blocks setExecuted(true); @@ -821,19 +822,27 @@ Algorithm_sptr Algorithm::createChildAlgorithm(const std::string &name, /** * Serialize this object to a string. The format is - * AlgorithmName.version(prop1=value1,prop2=value2,...) + * a json formatted string. * @returns This object serialized as a string */ std::string Algorithm::toString() const { - ::Json::Value root; ::Json::FastWriter writer; - ::Json::Reader reader; + + return writer.write(toJson()); +} + +/** +* Serialize this object to a json object) +* @returns This object serialized as a json object +*/ +::Json::Value Algorithm::toJson() const { + ::Json::Value root; root["name"] = name(); root["version"] = this->version(); root["properties"] = Kernel::PropertyManagerOwner::asJson(false); - return writer.write(root); + return root; } //-------------------------------------------------------------------------------------------- @@ -1568,6 +1577,30 @@ void Algorithm::reportCompleted(const double &duration, m_running = false; } +/** Registers the usage of the algorithm with the UsageService +@param enabled : true to enable logging, false to disable +*/ +void Algorithm::registerFeatureUsage(const float &duration) const +{ + if (ConfigService::Instance().UsageReporter().isEnabled()) { + ::Json::FastWriter writer; + ::Json::Value algJson = toJson(); + algJson["internal"] = isChild(); + std::string details = writer.write(algJson); + + //Limit details length for very long strings + const int STRING_SIZE_LIMIT = 60000; + if (details.size() > STRING_SIZE_LIMIT) + details.erase(STRING_SIZE_LIMIT, std::string::npos); + + std::ostringstream oss; + oss << this->name() << ".v" << this->version(); + ConfigService::Instance().UsageReporter().registerFeatureUsage("Algorithm", oss.str(), + DateAndTime::getCurrentTime(), + duration, details); + } +} + /** Enable or disable Logging of start and end messages @param enabled : true to enable logging, false to disable */ diff --git a/Framework/API/src/UsageService.cpp b/Framework/API/src/UsageService.cpp deleted file mode 100644 index cf3c9020fad..00000000000 --- a/Framework/API/src/UsageService.cpp +++ /dev/null @@ -1,227 +0,0 @@ -#include "MantidAPI/UsageService.h" -#include "MantidAPI/Algorithm.h" -#include "MantidAPI/AlgorithmManager.h" -#include "MantidKernel/ChecksumHelper.h" -#include "MantidKernel/ConfigService.h" -#include "MantidKernel/DateAndTime.h" -#include "MantidKernel/Exception.h" -#include "MantidKernel/InternetHelper.h" -#include "MantidKernel/MantidVersion.h" -#include "MantidKernel/Logger.h" -#include "MantidKernel/ParaViewVersion.h" - -#include <Poco/ActiveResult.h> -#include <json/json.h> - -namespace Mantid { -namespace API { - -using namespace Kernel; - -/// static logger -Kernel::Logger g_log("UsageService"); - -//---------------------------------------------------------------------------------------------- -/** FeatureUsage -*/ -FeatureUsage::FeatureUsage(const std::string &type, const std::string &name, - const Kernel::DateAndTime &start, - const float &duration, const std::string &details) - : type(type), name(name), start(start), duration(duration), - details(details) {} - -::Json::Value FeatureUsage::asJson() const { - ::Json::Value jsonMap; - jsonMap["type"] = type; - jsonMap["name"] = type; - jsonMap["start"] = start.toISO8601String(); - jsonMap["duration"] = duration; - jsonMap["details"] = details; - - return jsonMap; -} - -std::string FeatureUsage::asString() const { - ::Json::FastWriter writer; - return writer.write(asJson()); -} - -//---------------------------------------------------------------------------------------------- -/** Constructor for UsageServiceImpl - */ -UsageServiceImpl::UsageServiceImpl() - : m_timer(), m_timerTicks(0), m_timerTicksTarget(0), m_FeatureQueue(), - m_FeatureQueueSizeThreshold(50), m_mutex(), m_cachedHeader() { - if (isEnabled()) { - int interval = 60; - ConfigService::Instance().getValue("Usage.BufferCheck", interval); - - // set the ticks target to by 24 hours / interval - m_timerTicksTarget = 24 * 60 * 60 / interval; - - m_timer.setPeriodicInterval((interval * 1000)); - m_timer.start(Poco::TimerCallback<UsageServiceImpl>( - *this, &UsageServiceImpl::timerCallback)); - } -} - -//---------------------------------------------------------------------------------------------- -/** Destructor - */ -UsageServiceImpl::~UsageServiceImpl() {} - -void UsageServiceImpl::registerStartup() { - if (isEnabled()) { - sendStartupReport(); - } -} - -//---------------------------------------------------------------------------------------------- -/** registerFeatureUsage Overloads -*/ -void UsageServiceImpl::registerFeatureUsage(const std::string &type, - const std::string &name, - const Kernel::DateAndTime &start, - const float &duration, - const std::string &details) { - m_FeatureQueue.push(FeatureUsage(type, name, start, duration, details)); -} - -void UsageServiceImpl::registerFeatureUsage(const std::string &type, - const std::string &name, - const std::string &details) { - registerFeatureUsage(type, name, DateAndTime::getCurrentTime(), 0.0, details); -} - -void UsageServiceImpl::registerFeatureUsage(const Algorithm *alg, - const float &duration = 0.0) { - std::ostringstream oss; - oss << alg->name() << ".v" << alg->version(); - registerFeatureUsage("Algorithm", oss.str(), DateAndTime::getCurrentTime(), - duration, alg->toString()); -} - -//---------------------------------------------------------------------------------------------- - -bool UsageServiceImpl::isEnabled() const { - try { - int enabled = 0; - int retVal = Kernel::ConfigService::Instance().getValue( - "usagereports.enabled", enabled); - if ((retVal == 0) || (enabled == 0)) { - return false; // exit early - } - return true; - } catch (std::exception &ex) { - g_log.debug() - << "Error determining if usage reporting is enabled: assuming false. " - << ex.what() << std::endl; - return false; - } -} - -void UsageServiceImpl::flush() { - if (isEnabled()) { - sendFeatureUsageReport(); - } -} - -void UsageServiceImpl::sendStartupReport() { - try { - auto algSendStartupUsage = AlgorithmManager::Instance().create("SendUsage"); - algSendStartupUsage->setAlgStartupLogging(false); - Poco::ActiveResult<bool> result = algSendStartupUsage->executeAsync(); - } catch (Kernel::Exception::NotFoundError &) { - g_log.debug() << "SendUsage algorithm is not available - cannot update " - "send usage information." - << std::endl; - } catch (std::exception &ex) { - g_log.debug() << "SendUsage algorithm failure. " << ex.what() << std::endl; - } -} - -void UsageServiceImpl::sendFeatureUsageReport() { - try { - ::Json::Value root; - ::Json::FastWriter writer; - ::Json::Reader reader; - - if (!m_FeatureQueue.empty()) { - // lock around emptying of the Q so any further threads have to wait - Kernel::Mutex::ScopedLock _lock(m_mutex); - // generate json to submit - while (!m_FeatureQueue.empty()) { - auto featureUsage = m_FeatureQueue.front(); - root.append(featureUsage.asJson()); - m_FeatureQueue.pop(); - } - } - - if (root.size() > 0) { - std::string jsonString = writer.write(root); - g_log.debug() << "FeatureUsage to send\n" << jsonString << std::endl; - - // TODO - submit json to server - - } - - } catch (std::exception &ex) { - g_log.debug() << "sendFeatureUsageReport failure. " << ex.what() - << std::endl; - } -} - -void UsageServiceImpl::timerCallback(Poco::Timer &) { - m_timerTicks++; - if (m_timerTicks > m_timerTicksTarget) { - // send startup report - sendStartupReport(); - m_timerTicks = 0; - } - - // Check bufferlength - if (m_FeatureQueue.size() > m_FeatureQueueSizeThreshold) { - sendFeatureUsageReport(); - } -} - -/** -* This puts together the system information for the json document. -*/ -::Json::Value UsageServiceImpl::generateHeader() { - ::Json::Value header = m_cachedHeader; - - if (header.size() == 0) { - // username - header["uid"] = Kernel::ChecksumHelper::md5FromString( - ConfigService::Instance().getUsername()); - // hostname - header["host"] = Kernel::ChecksumHelper::md5FromString( - ConfigService::Instance().getComputerName()); - - // os name, version, and architecture - header["osName"] = ConfigService::Instance().getOSName(); - header["osArch"] = ConfigService::Instance().getOSArchitecture(); - header["osVersion"] = ConfigService::Instance().getOSVersion(); - header["osReadable"] = ConfigService::Instance().getOSVersionReadable(); - - // paraview version or zero - header["ParaView"] = ConfigService::Instance().pvPluginsAvailable() ? Kernel::ParaViewVersion::targetVersion() : 0; - - // mantid version and sha1 - header["mantidVersion"] = MantidVersion::version(); - header["mantidSha1"] = MantidVersion::revisionFull(); - - //cache this for future use - m_cachedHeader = header; - } - - // mantid version and sha1 - header["dateTime"] = DateAndTime::getCurrentTime().toISO8601String(); - - return header; -} - - -} // namespace API -} // namespace Mantid diff --git a/Framework/Kernel/CMakeLists.txt b/Framework/Kernel/CMakeLists.txt index 6b570abd6d1..eb1a3cefa28 100644 --- a/Framework/Kernel/CMakeLists.txt +++ b/Framework/Kernel/CMakeLists.txt @@ -102,6 +102,7 @@ set ( SRC_FILES src/UnitFactory.cpp src/UnitLabel.cpp src/UnitLabelTypes.cpp + src/UsageReporter.cpp src/UserCatalogInfo.cpp src/UserStringParser.cpp src/Utils.cpp @@ -192,7 +193,6 @@ set ( INC_FILES inc/MantidKernel/MRUList.h inc/MantidKernel/MagneticFormFactorTable.h inc/MantidKernel/MagneticIon.h - inc/MantidKernel/make_unique.h inc/MantidKernel/MandatoryValidator.h inc/MantidKernel/MantidVersion.h inc/MantidKernel/MaskedProperty.h @@ -262,6 +262,7 @@ set ( INC_FILES inc/MantidKernel/UnitFactory.h inc/MantidKernel/UnitLabel.h inc/MantidKernel/UnitLabelTypes.h + inc/MantidKernel/UsageReporter.h inc/MantidKernel/UserCatalogInfo.h inc/MantidKernel/UserStringParser.h inc/MantidKernel/Utils.h @@ -274,6 +275,7 @@ set ( INC_FILES inc/MantidKernel/WriteLock.h inc/MantidKernel/XMLInstantiator.h inc/MantidKernel/cow_ptr.h + inc/MantidKernel/make_unique.h ) set ( TEST_FILES @@ -382,6 +384,7 @@ set ( TEST_FILES UnitFactoryTest.h UnitLabelTest.h UnitTest.h + UsageReporterTest.h UserCatalogInfoTest.h UserStringParserTest.h UtilsTest.h diff --git a/Framework/Kernel/inc/MantidKernel/ConfigService.h b/Framework/Kernel/inc/MantidKernel/ConfigService.h index c3db1552f6e..753fbe0c860 100644 --- a/Framework/Kernel/inc/MantidKernel/ConfigService.h +++ b/Framework/Kernel/inc/MantidKernel/ConfigService.h @@ -40,6 +40,7 @@ namespace Kernel { class Logger; class FacilityInfo; class InstrumentInfo; +class UsageReporter; /** The ConfigService class provides a simple facade to access the Configuration functionality of the Mantid Framework. @@ -254,11 +255,15 @@ public: /// Gets the proxy for the system Kernel::ProxyInfo &getProxy(const std::string &url); + /// Gets access to the UsageReporter + Mantid::Kernel::UsageReporter& UsageReporter(); + private: friend struct Mantid::Kernel::CreateUsingNew<ConfigServiceImpl>; /// Handles distribution of Poco signals. mutable Poco::NotificationCenter m_notificationCenter; + void setupUsageReporting(); // Private constructors and destructor for singleton class ConfigServiceImpl(); /// Private copy constructor. Prevents singleton being copied. @@ -341,6 +346,8 @@ private: Kernel::ProxyInfo m_proxyInfo; /// wether the proxy has been populated yet bool m_isProxySet; + ///Usage Reporter + Mantid::Kernel::UsageReporter *m_usage_reporter; }; /// Forward declaration of a specialisation of SingletonHolder for diff --git a/Framework/API/inc/MantidAPI/UsageService.h b/Framework/Kernel/inc/MantidKernel/UsageReporter.h similarity index 68% rename from Framework/API/inc/MantidAPI/UsageService.h rename to Framework/Kernel/inc/MantidKernel/UsageReporter.h index 5f0b94d594f..57f0d7ff9b5 100644 --- a/Framework/API/inc/MantidAPI/UsageService.h +++ b/Framework/Kernel/inc/MantidKernel/UsageReporter.h @@ -1,28 +1,23 @@ -#ifndef MANTID_API_USAGESERVICE_H_ -#define MANTID_API_USAGESERVICE_H_ +#ifndef MANTID_KERNEL_USAGEREPORTER_H_ +#define MANTID_KERNEL_USAGEREPORTER_H_ -#include "MantidAPI/DllConfig.h" +#include "MantidKernel/DllConfig.h" #include "MantidKernel/DateAndTime.h" -#include "MantidKernel/SingletonHolder.h" #include "MantidKernel/MultiThreaded.h" -#include <Poco/Timer.h> + #include <json/value.h> -#include <queue> -namespace Json -{ -class Value; -} +#include <Poco/ActiveResult.h> +#include <Poco/Timer.h> + +#include <queue> namespace Mantid { -namespace API { +namespace Kernel { -class Algorithm; - -/** UsageService : The Usage Service is responsible for collating, and sending +/** UsageReporter : The Usage reporter is responsible for collating, and sending all usage data. This centralizes all the logic covering Usage Reporting including: - - Detecting if reporting is enabled - Registering the startup of Mantid - Sending Startup usage reports, immediately, and every 24 hours thereafter @@ -56,7 +51,7 @@ class FeatureUsage { public: /// Constructor FeatureUsage(const std::string &type, const std::string &name, - const Kernel::DateAndTime &start, const float& duration, + const Kernel::DateAndTime &start, const float &duration, const std::string &details); ::Json::Value asJson() const; @@ -69,46 +64,56 @@ public: std::string details; }; -class MANTID_API_DLL UsageServiceImpl { +class MANTID_KERNEL_DLL UsageReporter { public: + /// Sets the interval that the timer checks for tasks + void setInterval(const uint32_t seconds = 60); /// Registers the Startup of Mantid void registerStartup(); /// Registers the use of a feature in mantid void registerFeatureUsage(const std::string &type, const std::string &name, const Kernel::DateAndTime &start, - const float& duration, const std::string &details); + const float &duration, const std::string &details); void registerFeatureUsage(const std::string &type, const std::string &name, const std::string &details = ""); - void registerFeatureUsage(const Algorithm* alg, const float& duration); /// Returns true if usage reporting is enabled bool isEnabled() const; + /// Sets whether the UsageReporter is enabled + void setEnabled(const bool enabled); /// flushes any buffers and sends any outstanding usage reports void flush(); -private: - friend struct Mantid::Kernel::CreateUsingNew<UsageServiceImpl>; - /// Constructor - UsageServiceImpl(); + UsageReporter(); + /// Destructor + ~UsageReporter(); +private: /// Private, unimplemented copy constructor - UsageServiceImpl(const UsageServiceImpl &); + UsageReporter(const UsageReporter &); /// Private, unimplemented copy assignment operator - UsageServiceImpl &operator=(const UsageServiceImpl &); - /// Destructor - ~UsageServiceImpl(); - + UsageReporter &operator=(const UsageReporter &); + /// Send startup Report void sendStartupReport(); /// Send featureUsageReport - void sendFeatureUsageReport(); + void sendFeatureUsageReport(const bool synchronous); /// A method to handle the timerCallbacks void timerCallback(Poco::Timer &); - /// Generate jsonfor calls to usage service + /// Generate json for calls to usage service ::Json::Value generateHeader(); - + /// generates the message body for a startup message + std::string generateStartupMessage(); + /// generates the message body for a feature usage message + std::string generateFeatureUsageMessage(); + Poco::ActiveResult<int> sendStartupAsync(const std::string &message); + int sendStartupAsyncImpl(const std::string &message); + Poco::ActiveResult<int> sendFeatureAsync(const std::string &message); + int sendFeatureAsyncImpl(const std::string &message); + /// sends a report over the internet + int sendReport(const std::string &message, const std::string &url); /// a timer Poco::Timer m_timer; @@ -119,22 +124,13 @@ private: std::queue<FeatureUsage> m_FeatureQueue; size_t m_FeatureQueueSizeThreshold; + bool m_isEnabled; mutable Kernel::Mutex m_mutex; ::Json::Value m_cachedHeader; }; -/// Forward declaration of a specialization of SingletonHolder for -/// UsageServiceImpl (needed for dllexport/dllimport) and a typedef for -/// it. -#ifdef _WIN32 -// this breaks new namespace declaration rules; need to find a better fix -template class MANTID_API_DLL Mantid::Kernel::SingletonHolder<UsageServiceImpl>; -#endif /* _WIN32 */ -typedef MANTID_API_DLL Mantid::Kernel::SingletonHolder<UsageServiceImpl> - UsageService; - } // namespace API } // namespace Mantid -#endif /* MANTID_API_USAGESERVICE_H_ */ \ No newline at end of file +#endif /* MANTID_KERNEL_USAGEREPORTER_H_ */ \ No newline at end of file diff --git a/Framework/Kernel/src/ConfigService.cpp b/Framework/Kernel/src/ConfigService.cpp index cb7b0e1b59f..e994fb3557a 100644 --- a/Framework/Kernel/src/ConfigService.cpp +++ b/Framework/Kernel/src/ConfigService.cpp @@ -11,6 +11,7 @@ #include "MantidKernel/Exception.h" #include "MantidKernel/FacilityInfo.h" #include "MantidKernel/NetworkProxy.h" +#include "MantidKernel/UsageReporter.h" #include <Poco/Util/LoggingConfigurator.h> #include <Poco/Util/SystemConfiguration.h> @@ -139,6 +140,20 @@ private: // Private member functions //------------------------------- +void ConfigServiceImpl::setupUsageReporting() { + m_usage_reporter = new Mantid::Kernel::UsageReporter(); + + int enabled = 0; + int interval = 0; + int retVal = getValue("Usage.BufferCheckInterval", interval); + if ((retVal == 1) && (interval > 0)) { + m_usage_reporter->setInterval(interval); + } + retVal = getValue("usagereports.enabled", enabled); + m_usage_reporter->setEnabled((retVal == 0) || (enabled == 0)); + m_usage_reporter->registerStartup(); +} + /// Private constructor for singleton class ConfigServiceImpl::ConfigServiceImpl() : m_pConf(NULL), m_pSysConfig(NULL), m_changed_keys(), m_ConfigPaths(), @@ -146,13 +161,14 @@ ConfigServiceImpl::ConfigServiceImpl() m_properties_file_name("Mantid.properties"), #ifdef MPI_BUILD // Use a different user properties file for an mpi-enabled build to avoid - // confusion if both are used on the same filesystem + // confusion if both are used on the same file system m_user_properties_file_name("Mantid-mpi.user.properties"), #else m_user_properties_file_name("Mantid.user.properties"), #endif m_DataSearchDirs(), m_UserSearchDirs(), m_InstrumentDirs(), - m_instr_prefixes(), m_proxyInfo(), m_isProxySet(false) { + m_instr_prefixes(), m_proxyInfo(), m_isProxySet(false), + m_usage_reporter(NULL) { // getting at system details m_pSysConfig = new WrappedObject<Poco::Util::SystemConfiguration>; m_pConf = 0; @@ -278,12 +294,17 @@ ConfigServiceImpl::ConfigServiceImpl() } // must update the cache of instrument paths cacheInstrumentPaths(); + + // setup usage reporting + setupUsageReporting(); } /** Private Destructor * Prevents client from calling 'delete' on the pointer handed out by Instance */ ConfigServiceImpl::~ConfigServiceImpl() { + //clear up the usage reporter first + delete m_usage_reporter; // std::cerr << "ConfigService destroyed." << std::endl; Kernel::Logger::shutdown(); delete m_pSysConfig; @@ -632,18 +653,22 @@ void ConfigServiceImpl::createUserPropertiesFile() const { std::fstream::out); filestr << "# This file can be used to override any properties for this " - "installation." << std::endl; + "installation." + << std::endl; filestr << "# Any properties found in this file will override any that are " - "found in the Mantid.Properties file" << std::endl; + "found in the Mantid.Properties file" + << std::endl; filestr << "# As this file will not be replaced with futher installations " - "of Mantid it is a safe place to put " << std::endl; + "of Mantid it is a safe place to put " + << std::endl; filestr << "# properties that suit your particular installation." << std::endl; filestr << "#" << std::endl; filestr << "# See here for a list of possible options:" << std::endl; filestr << "# " "http://www.mantidproject.org/" - "Properties_File#Mantid.User.Properties" << std::endl; + "Properties_File#Mantid.User.Properties" + << std::endl; filestr << std::endl; filestr << "##" << std::endl; filestr << "## GENERAL" << std::endl; @@ -678,11 +703,13 @@ void ConfigServiceImpl::createUserPropertiesFile() const { filestr << "##" << std::endl; filestr << std::endl; filestr << "## Sets a list of directories (separated by semi colons) to " - "search for data" << std::endl; + "search for data" + << std::endl; filestr << "#datasearch.directories=../data;../isis/data" << std::endl; filestr << std::endl; filestr << "## Set a list (separated by semi colons) of directories to " - "look for additional Python scripts" << std::endl; + "look for additional Python scripts" + << std::endl; filestr << "#pythonscripts.directories=../scripts;../docs/MyScripts" << std::endl; filestr << std::endl; @@ -723,7 +750,8 @@ void ConfigServiceImpl::createUserPropertiesFile() const { filestr << "#MantidOptions.ReusePlotInstances=Off" << std::endl; filestr << std::endl; filestr << "## Uncomment to disable use of OpenGL to render unwrapped " - "instrument views" << std::endl; + "instrument views" + << std::endl; filestr << "#MantidOptions.InstrumentView.UseOpenGL=Off" << std::endl; filestr.close(); @@ -1958,9 +1986,17 @@ Kernel::ProxyInfo &ConfigServiceImpl::getProxy(const std::string &url) { return m_proxyInfo; } -/** Sets the log level priority for the File log channel -* @param logLevel the integer value of the log level to set, 1=Critical, 7=Debug +/** Gets a reference to the UsageReporter +* @returns a reference to the UsageReporter class */ +UsageReporter& ConfigServiceImpl::UsageReporter() +{ + return *m_usage_reporter; +} + + /** Sets the log level priority for the File log channel + * @param logLevel the integer value of the log level to set, 1=Critical, 7=Debug + */ void ConfigServiceImpl::setFileLogLevel(int logLevel) { setFilterChannelLogLevel("fileFilterChannel", logLevel); } diff --git a/Framework/Kernel/src/UsageReporter.cpp b/Framework/Kernel/src/UsageReporter.cpp new file mode 100644 index 00000000000..9b847b1ba73 --- /dev/null +++ b/Framework/Kernel/src/UsageReporter.cpp @@ -0,0 +1,314 @@ +#include "MantidKernel/UsageReporter.h" +#include "MantidKernel/ChecksumHelper.h" +#include "MantidKernel/ConfigService.h" +#include "MantidKernel/DateAndTime.h" +#include "MantidKernel/Exception.h" +#include "MantidKernel/InternetHelper.h" +#include "MantidKernel/MantidVersion.h" +#include "MantidKernel/Logger.h" +#include "MantidKernel/ParaViewVersion.h" + +#include <Poco/ActiveMethod.h> +#include <json/json.h> +#include <stdlib.h> + +namespace Mantid { +namespace Kernel { + +/// static logger +Kernel::Logger g_log("UsageReporter"); + +// const std::string STARTUP_URL("http://reports.mantidproject.org/api/usage"); +const std::string STARTUP_URL( + "http://posttestserver.com/post.php?dir=Mantid"); // dev location + // http://posttestserver.com/data/ +// const std::string FEATURE_URL("http://reports.mantidproject.org/api/usage"); +const std::string FEATURE_URL( + "http://posttestserver.com/post.php?dir=Mantid"); // dev location + +//---------------------------------------------------------------------------------------------- +/** FeatureUsage +*/ +FeatureUsage::FeatureUsage(const std::string &type, const std::string &name, + const Kernel::DateAndTime &start, + const float &duration, const std::string &details) + : type(type), name(name), start(start), duration(duration), + details(details) {} + +::Json::Value FeatureUsage::asJson() const { + ::Json::Value jsonMap; + jsonMap["type"] = type; + jsonMap["name"] = name; + jsonMap["start"] = start.toISO8601String(); + jsonMap["duration"] = duration; + jsonMap["details"] = details; + + return jsonMap; +} + +std::string FeatureUsage::asString() const { + ::Json::FastWriter writer; + return writer.write(asJson()); +} + +//---------------------------------------------------------------------------------------------- +/** Constructor for UsageReporter + */ +UsageReporter::UsageReporter() + : m_timer(), m_timerTicks(0), m_timerTicksTarget(0), m_FeatureQueue(), + m_FeatureQueueSizeThreshold(50), m_isEnabled(false), m_mutex(), + m_cachedHeader() { + setInterval(60); +} + +//---------------------------------------------------------------------------------------------- +/** Destructor + */ +UsageReporter::~UsageReporter() { + try { + try { + // stop the timer + m_timer.restart(0); + // send any remaining feature usage records + flush(); + } catch (std::exception &ex) { + g_log.error() << "Error during the closedown of the UsageService. " + << ex.what(); + } + } catch (...) { // do not allow exceptions to leave the destructor + } +} + +void UsageReporter::setInterval(const uint32_t seconds) { + // set the ticks target to by 24 hours / interval + m_timerTicksTarget = 24 * 60 * 60 / seconds; + + m_timer.setPeriodicInterval((seconds * 1000)); + if (isEnabled()) { + m_timer.start(Poco::TimerCallback<UsageReporter>( + *this, &UsageReporter::timerCallback)); + } +} + +void UsageReporter::registerStartup() { + if (isEnabled()) { + sendStartupReport(); + } +} + +//---------------------------------------------------------------------------------------------- +/** registerFeatureUsage Overloads +*/ +void UsageReporter::registerFeatureUsage(const std::string &type, + const std::string &name, + const Kernel::DateAndTime &start, + const float &duration, + const std::string &details) { + m_FeatureQueue.push(FeatureUsage(type, name, start, duration, details)); +} + +void UsageReporter::registerFeatureUsage(const std::string &type, + const std::string &name, + const std::string &details) { + registerFeatureUsage(type, name, DateAndTime::getCurrentTime(), 0.0, details); +} + +//---------------------------------------------------------------------------------------------- + +bool UsageReporter::isEnabled() const { return m_isEnabled; } + +void UsageReporter::setEnabled(const bool enabled) { + if (m_isEnabled != enabled) { + if (enabled) { + m_timer.start(Poco::TimerCallback<UsageReporter>( + *this, &UsageReporter::timerCallback)); + } else { + m_timer.stop(); + } + } + m_isEnabled = enabled; +} + +void UsageReporter::flush() { + if (isEnabled()) { + sendFeatureUsageReport(true); + } +} + +void UsageReporter::sendStartupReport() { + try { + std::string message = this->generateStartupMessage(); + + // send the report + // sendReport(message, STARTUP_URL); + Poco::ActiveResult<int> result = this->sendStartupAsync(message); + } catch (std::exception &ex) { + g_log.debug() << "Send startup usage failure. " << ex.what() << std::endl; + } +} + +void UsageReporter::sendFeatureUsageReport(const bool synchronous = false) { + try { + std::string message = this->generateFeatureUsageMessage(); + //g_log.debug() << "FeatureUsage to send\n" << message << std::endl; + if (!message.empty()) { + if (synchronous) { + sendFeatureAsyncImpl(message); + } else { + Poco::ActiveResult<int> result = this->sendFeatureAsync(message); + } + } + + } catch (std::exception &ex) { + g_log.debug() << "sendFeatureUsageReport failure. " << ex.what() + << std::endl; + } +} + +void UsageReporter::timerCallback(Poco::Timer &) { + m_timerTicks++; + if (m_timerTicks > m_timerTicksTarget) { + // send startup report + sendStartupReport(); + m_timerTicks = 0; + } + + // Check bufferlength + if (m_FeatureQueue.size() > m_FeatureQueueSizeThreshold) { + sendFeatureUsageReport(); + } +} + +/** +* This puts together the system information for the json document. +*/ +::Json::Value UsageReporter::generateHeader() { + ::Json::Value header = m_cachedHeader; + + if (header.size() == 0) { + // username + header["uid"] = Kernel::ChecksumHelper::md5FromString( + ConfigService::Instance().getUsername()); + // hostname + header["host"] = Kernel::ChecksumHelper::md5FromString( + ConfigService::Instance().getComputerName()); + + // os name, version, and architecture + header["osName"] = ConfigService::Instance().getOSName(); + header["osArch"] = ConfigService::Instance().getOSArchitecture(); + header["osVersion"] = ConfigService::Instance().getOSVersion(); + header["osReadable"] = ConfigService::Instance().getOSVersionReadable(); + + // paraview version or zero + if (ConfigService::Instance().pvPluginsAvailable()) { + header["ParaView"] = Kernel::ParaViewVersion::targetVersion(); + } else { + header["ParaView"] = 0; + } + + // mantid version and sha1 + header["mantidVersion"] = MantidVersion::version(); + header["mantidSha1"] = MantidVersion::revisionFull(); + + // cache this for future use + m_cachedHeader = header; + } + + // mantid version and sha1 + header["dateTime"] = DateAndTime::getCurrentTime().toISO8601String(); + + return header; +} + +std::string UsageReporter::generateStartupMessage() { + auto message = this->generateHeader(); + + // get the properties that were set# + message["application"] = "mantidplot"; + + // The server possibly knows about component + // but we are not using it here. + // message["component"] = ""; + + ::Json::FastWriter writer; + return writer.write(message); +} + +std::string UsageReporter::generateFeatureUsageMessage() { + + auto message = this->generateHeader(); + ::Json::FastWriter writer; + ::Json::Value features; + size_t featureCount = 0; + + if (!m_FeatureQueue.empty()) { + // lock around emptying of the Q so any further threads have to wait + Kernel::Mutex::ScopedLock _lock(m_mutex); + // generate json to submit + while (!m_FeatureQueue.empty()) { + auto featureUsage = m_FeatureQueue.front(); + features.append(featureUsage.asJson()); + m_FeatureQueue.pop(); + featureCount++; + } + } + + if (featureCount > 0) { + message["features"] = features; + return writer.write(message); + } + return ""; +} + +//-------------------------------------------------------------------------------------------- +/** +* Asynchronous execution +*/ +Poco::ActiveResult<int> +UsageReporter::sendStartupAsync(const std::string &message) { + auto sendAsync = new Poco::ActiveMethod<int, std::string, UsageReporter>( + this, &UsageReporter::sendStartupAsyncImpl); + return (*sendAsync)(message); +} + +/**Async method for sending startup messages +*/ +int UsageReporter::sendStartupAsyncImpl(const std::string &message) { + return this->sendReport(message, STARTUP_URL); +} + +/**Async method for sending feature messages +*/ +Poco::ActiveResult<int> +UsageReporter::sendFeatureAsync(const std::string &message) { + auto sendAsync = new Poco::ActiveMethod<int, std::string, UsageReporter>( + this, &UsageReporter::sendFeatureAsyncImpl); + return (*sendAsync)(message); +} + +/**Async method for sending feature messages +*/ +int UsageReporter::sendFeatureAsyncImpl(const std::string &message) { + return this->sendReport(message, FEATURE_URL); +} + +int UsageReporter::sendReport(const std::string &message, + const std::string &url) { + int status = -1; + try { + Kernel::InternetHelper helper; + std::stringstream responseStream; + helper.setTimeout(2); + helper.setBody(message); + status = helper.sendRequest(url, responseStream); + } catch (Mantid::Kernel::Exception::InternetError &e) { + status = e.errorCode(); + g_log.information() << "Call to \"" << url << "\" responded with " << status + << "\n" << e.what() << "\n"; + } + + return status; +} + +} // namespace API +} // namespace Mantid diff --git a/Framework/API/test/UsageServiceTest.h b/Framework/Kernel/test/UsageReporterTest.h similarity index 88% rename from Framework/API/test/UsageServiceTest.h rename to Framework/Kernel/test/UsageReporterTest.h index be29c66e815..db018867748 100644 --- a/Framework/API/test/UsageServiceTest.h +++ b/Framework/Kernel/test/UsageReporterTest.h @@ -3,9 +3,9 @@ #include <cxxtest/TestSuite.h> -#include "MantidAPI/UsageService.h" +#include "MantidKernel/UsageReporter.h" -using Mantid::API::UsageServiceImpl; +using Mantid::Kernel::UsageReporter; class UsageServiceTest : public CxxTest::TestSuite { public: -- GitLab