Mantid
Loading...
Searching...
No Matches
UsageService.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8#include "MantidJson/Json.h"
14#include "MantidKernel/Logger.h"
16
17#include <Poco/ActiveResult.h>
18#include <Poco/String.h>
19#include <algorithm>
20#include <boost/algorithm/string/join.hpp>
21
22#include <json/json.h>
23
24namespace {
25constexpr auto SEPARATOR = "->";
26}
27
28namespace Mantid::Kernel {
29
31Kernel::Logger g_log("UsageServiceImpl");
32
33//----------------------------------------------------------------------------------------------
36FeatureUsage::FeatureUsage(const FeatureType &type, std::string name, const bool internal, std::string application)
37 : type(type), name(std::move(name)), internal(internal), application(std::move(application)) {}
38
39// Better brute force.
41 if (type < r.type)
42 return true;
43 if (type > r.type)
44 return false;
45 // Otherwise type are equal
46 if (name < r.name)
47 return true;
48 if (name > r.name)
49 return false;
50 // Otherwise name are equal
51 if (static_cast<int>(internal) < static_cast<int>(r.internal))
52 return true;
53 if (static_cast<int>(internal) > static_cast<int>(r.internal))
54 return false;
55 // Otherwise all are equal
56 return false;
57}
58
61
62 switch (type) {
64 return "Algorithm";
66 return "Feature";
68 return "Interface";
70 return "Function";
71 }
72 return "Unknown";
73}
74
75::Json::Value FeatureUsage::asJson() const {
76 ::Json::Value retVal;
77
78 retVal["type"] = featureTypeToString();
79 retVal["name"] = name;
80 retVal["internal"] = internal;
81 retVal["application"] = application;
82
83 return retVal;
84}
85
86//----------------------------------------------------------------------------------------------
90 : m_timer(), m_timerTicks(0), m_timerTicksTarget(0), m_FeatureQueue(), m_FeatureQueueSizeThreshold(50),
91 m_isEnabled(false), m_mutex(), m_application("python"), m_startTime(Types::Core::DateAndTime::getCurrentTime()),
92 m_startupActiveMethod(this, &UsageServiceImpl::sendStartupAsyncImpl),
93 m_featureActiveMethod(this, &UsageServiceImpl::sendFeatureAsyncImpl) {
94 setInterval(60);
95 auto url = Mantid::Kernel::ConfigService::Instance().getValue<std::string>("usagereports.rooturl");
96 if (!url.has_value()) {
97 g_log.debug() << "Failed to load usage report url\n";
98 } else {
99 m_url = url.value();
100 g_log.debug() << "Root usage reporting url is " << m_url << "\n";
101 };
102}
103
105
106const std::string &UsageServiceImpl::getApplicationName() const { return m_application; }
107
108void UsageServiceImpl::setInterval(const uint32_t seconds) {
109 // set the ticks target to by 24 hours / interval
110 m_timerTicksTarget = 24 * 60 * 60 / seconds;
111
112 m_timer.setStartInterval((seconds * 1000));
113 m_timer.setPeriodicInterval((seconds * 1000));
114}
115
117 if (isEnabled()) {
119 }
120}
121
124void UsageServiceImpl::registerFeatureUsage(const FeatureType &type, const std::vector<std::string> &name,
125 const bool internal) {
126 if (isEnabled()) {
127 std::lock_guard<std::mutex> _lock(m_mutex);
128
129 using boost::algorithm::join;
130 m_FeatureQueue.push(FeatureUsage(type, join(name, SEPARATOR), internal, getApplicationName()));
131 }
132}
133
134void UsageServiceImpl::registerFeatureUsage(const FeatureType &type, const std::string &name, const bool internal) {
135 if (isEnabled()) {
136 std::lock_guard<std::mutex> _lock(m_mutex);
137 m_FeatureQueue.push(FeatureUsage(type, name, internal, getApplicationName()));
138 }
139}
140
141void UsageServiceImpl::registerFeatureUsage(const FeatureType &type, std::initializer_list<std::string> name,
142 const bool internal) {
143
144 registerFeatureUsage(type, std::vector<std::string>(name), internal);
145}
146
148
149void UsageServiceImpl::setEnabled(const bool enabled) {
150 if (m_isEnabled != enabled) {
151 if (enabled) {
152 m_timer.start(Poco::TimerCallback<UsageServiceImpl>(*this, &UsageServiceImpl::timerCallback));
153 } else {
154 m_timer.stop();
155 }
156 }
157 m_isEnabled = enabled;
158}
159
161 std::queue<FeatureUsage> empty;
162 std::swap(m_FeatureQueue, empty);
163}
164
166 if (isEnabled()) {
168 }
169}
170
174Types::Core::time_duration UsageServiceImpl::getUpTime() {
175 return Types::Core::DateAndTime::getCurrentTime() - m_startTime;
176}
177
179 try {
180 // stop the timer
181 setEnabled(false);
182 // send any remaining feature usage records
184 } catch (std::exception &ex) {
185 g_log.error() << "Error during the shutdown of the UsageService. " << ex.what();
186 }
187}
188
190 try {
191 std::string message = this->generateStartupMessage();
192 // send the report
194 } catch (std::exception &ex) {
195 g_log.debug() << "Send startup usage failure. " << ex.what() << '\n';
196 }
197}
198
199void UsageServiceImpl::sendFeatureUsageReport(const bool synchronous = false) {
200 try {
201 std::string message = this->generateFeatureUsageMessage();
202 if (!message.empty()) {
203 if (synchronous) {
204 sendFeatureAsyncImpl(message);
205 } else {
207 }
208 }
209
210 } catch (std::exception &ex) {
211 g_log.debug() << "sendFeatureUsageReport failure. " << ex.what() << '\n';
212 }
213}
214
215void UsageServiceImpl::timerCallback(Poco::Timer & /*unused*/) {
216 m_timerTicks++;
218 // send startup report
220 m_timerTicks = 0;
221 }
222
223 // Check bufferlength
226 }
227}
228
233 ::Json::Value header;
234
235 // mantid version and sha1
236 header["mantidVersion"] = MantidVersion::versionShort();
237
238 return header;
239}
240
245 ::Json::Value message;
246
247 // username
248 message["uid"] = Kernel::ChecksumHelper::md5FromString(ConfigService::Instance().getUsername());
249 // hostname
250 message["host"] = Kernel::ChecksumHelper::md5FromString(ConfigService::Instance().getComputerName());
251
252 // os name, version, and architecture
253 message["osName"] = ConfigService::Instance().getOSName();
254 message["osArch"] = ConfigService::Instance().getOSArchitecture();
255 message["osVersion"] = ConfigService::Instance().getOSVersion();
256 message["osReadable"] = ConfigService::Instance().getOSVersionReadable();
257
258 // legacy interface requires paraview version DON'T REMOVE
259 message["ParaView"] = 0;
260
261 // mantid version and sha1
262 message["mantidVersion"] = MantidVersion::version();
263 message["mantidSha1"] = MantidVersion::revisionFull();
264
265 // mantid version and sha1
266 message["dateTime"] = m_startTime.toISO8601String();
267
268 message["application"] = m_application;
269
270 return Mantid::JsonHelpers::jsonToString(message);
271}
272
274
275 std::map<FeatureUsage, int> featureCountMap;
276
277 if (!m_FeatureQueue.empty()) {
278 // lock around emptying of the Q so any further threads have to wait
279 std::lock_guard<std::mutex> _lock(m_mutex);
280 // generate a map containing the counts of identical feature usage records
281 while (!m_FeatureQueue.empty()) {
282 auto featureUsage = m_FeatureQueue.front();
283 m_FeatureQueue.pop();
284 if (featureCountMap.find(featureUsage) == featureCountMap.end()) {
285 featureCountMap[featureUsage] = 1;
286 } else {
287 featureCountMap[featureUsage]++;
288 }
289 }
290 }
291
292 if (!featureCountMap.empty()) {
293 ::Json::Value features;
294 auto message = this->generateFeatureHeader();
295 for (auto const &featureItem : featureCountMap) {
296 ::Json::Value thisFeature = featureItem.first.asJson();
297 thisFeature["count"] = featureItem.second;
298 features.append(thisFeature);
299 }
300 if (!features.empty()) {
301 message["features"] = features;
302 return Mantid::JsonHelpers::jsonToString(message);
303 }
304 }
305 return "";
306}
307
308//--------------------------------------------------------------------------------------------
316 return this->sendReport(message, m_url + "/api/usage");
317}
318
322 return this->sendReport(message, m_url + "/api/feature");
323}
324
325InternetHelper::HTTPStatus UsageServiceImpl::sendReport(const std::string &message, const std::string &url) {
327 try {
329 std::stringstream responseStream;
330 helper.setTimeout(20);
331 helper.setBody(message);
332 status = helper.sendRequest(url, responseStream);
334 status = static_cast<InternetHelper::HTTPStatus>(e.errorCode());
335 g_log.information() << "Call to \"" << url << "\" responded with " << static_cast<int>(status) << "\n"
336 << e.what() << "\n";
337 }
338
339 return status;
340}
341
342} // namespace Mantid::Kernel
std::string name
Definition Run.cpp:60
Exception thrown when error occurs accessing an internet resource.
Definition Exception.h:321
const char * what() const noexcept override
Overloaded reporting method.
const int & errorCode() const
Writes out the range and limits.
UsageReporter : The Usage reporter is responsible for collating, and sending all usage data.
std::string featureTypeToString() const
Convert the stored feature type enum to a string.
::Json::Value asJson() const
bool operator<(const FeatureUsage &r) const
FeatureUsage(const FeatureType &type, std::string name, const bool internal, std::string application)
Constructor.
InternetHelper : A helper class for supporting access to resources through HTTP and HTTPS.
void setBody(const std::string &body)
Sets the body & content length for future requests, this will also set the method to POST is the body...
void setTimeout(int seconds)
Sets the timeout in seconds.
virtual HTTPStatus sendRequest(const std::string &url, std::ostream &responseStream)
Performs a request using http or https depending on the url.
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
static const char * version()
The full version number.
static const char * versionShort()
The version number of the last full version.
static const char * revisionFull()
The full SHA-1 of the last commit.
InternetHelper::HTTPStatus sendFeatureAsyncImpl(const std::string &message)
Async method for sending feature messages.
Poco::ActiveMethod< InternetHelper::HTTPStatus, std::string, UsageServiceImpl > m_featureActiveMethod
Async method for sending feature notifications.
void setInterval(const uint32_t seconds=60)
Sets the interval that the timer checks for tasks.
const std::string & getApplicationName() const
Returns the application name that has invoked Mantid.
void registerFeatureUsage(const FeatureType &type, const std::vector< std::string > &name, const bool internal)
registerFeatureUsage registers the use of a feature in mantid.
void setApplicationName(const std::string &name)
Sets the application name that has invoked Mantid.
void setEnabled(const bool enabled)
Sets whether the UsageReporter is enabled.
std::queue< FeatureUsage > m_FeatureQueue
void registerStartup()
Registers the Startup of Mantid.
void sendStartupReport()
Send startup Report.
uint32_t m_timerTicksTarget
The number of timer ticks at which to reset.
InternetHelper::HTTPStatus sendStartupAsyncImpl(const std::string &message)
Asynchronous execution.
Types::Core::DateAndTime m_startTime
void timerCallback(Poco::Timer &)
A method to handle the timerCallbacks.
virtual std::string generateFeatureUsageMessage()
generates the message body for a feature usage message
::Json::Value generateFeatureHeader()
This puts together the system information for the json document.
uint32_t m_timerTicks
The number of timer ticks since the last reset.
Poco::ActiveMethod< InternetHelper::HTTPStatus, std::string, UsageServiceImpl > m_startupActiveMethod
Async method for sending startup notifications.
void sendFeatureUsageReport(const bool synchronous)
Send featureUsageReport.
std::string m_url
Stores the base url of the usage system.
void flush()
flushes any buffers and sends any outstanding usage reports
bool isEnabled() const
Returns true if usage reporting is enabled.
Types::Core::time_duration getUpTime()
gets the uptime of this mantid instance
virtual Kernel::InternetHelper::HTTPStatus sendReport(const std::string &message, const std::string &url)
sends a report over the internet
void clear()
clear any buffers without sending any outstanding usage reports
virtual std::string generateStartupMessage()
generates the message body for a startup message
MANTID_KERNEL_DLL std::string md5FromString(const std::string &input)
create a md5 checksum from a string
FeatureType
An enum specifying the 4 possible features types that can be logged in the usage service.
STL namespace.