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.is_initialized()) {
97 g_log.debug() << "Failed to load usage report url\n";
98 } else {
99 m_url = url.get();
100 g_log.debug() << "Root usage reporting url is " << m_url << "\n";
101 };
102}
103
104void UsageServiceImpl::setApplicationName(const std::string &name) { m_application = name; }
105
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
Exception thrown when error occurs accessing an internet resource.
Definition: Exception.h:321
const char * what() const noexcept override
Overloaded reporting method.
Definition: Exception.cpp:311
const int & errorCode() const
Writes out the range and limits.
Definition: Exception.cpp:317
UsageReporter : The Usage reporter is responsible for collating, and sending all usage data.
Definition: UsageService.h:41
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:52
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
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.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
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.
Definition: UsageService.h:146
void setInterval(const uint32_t seconds=60)
Sets the interval that the timer checks for tasks.
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
Definition: UsageService.h:136
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.
Definition: UsageService.h:134
std::string getApplicationName() const
Returns the application name that has invoked Mantid.
InternetHelper::HTTPStatus sendStartupAsyncImpl(const std::string &message)
Asynchronous execution.
Types::Core::DateAndTime m_startTime
Definition: UsageService.h:141
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.
Definition: UsageService.h:132
Poco::ActiveMethod< InternetHelper::HTTPStatus, std::string, UsageServiceImpl > m_startupActiveMethod
Async method for sending startup notifications.
Definition: UsageService.h:144
void sendFeatureUsageReport(const bool synchronous)
Send featureUsageReport.
std::string m_url
Stores the base url of the usage system.
Definition: UsageService.h:149
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.
Definition: UsageService.h:28
STL namespace.