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