diff --git a/Framework/Kernel/CMakeLists.txt b/Framework/Kernel/CMakeLists.txt index 56ab06f8885661c12027d8c84fda57409fc4032a..1b86ea4abec0bc0d6f7a1d980f04bc638962ceed 100644 --- a/Framework/Kernel/CMakeLists.txt +++ b/Framework/Kernel/CMakeLists.txt @@ -15,6 +15,7 @@ set ( SRC_FILES src/ConfigService.cpp src/ConfigObserver.cpp src/ConfigPropertyObserver.cpp + src/CrashService.cpp src/DateAndTime.cpp src/DataItem.cpp src/DateAndTimeHelpers.cpp @@ -166,6 +167,7 @@ set ( INC_FILES inc/MantidKernel/ConfigService.h inc/MantidKernel/ConfigObserver.h inc/MantidKernel/ConfigPropertyObserver.h + inc/MantidKernel/CrashService.h inc/MantidKernel/DateAndTime.h inc/MantidKernel/DataItem.h inc/MantidKernel/DataService.h @@ -340,6 +342,7 @@ set ( TEST_FILES ConfigServiceTest.h ConfigObserverTest.h ConfigPropertyObserverTest.h + CrashServiceTest.h CowPtrTest.h DataServiceTest.h DateAndTimeHelpersTest.h diff --git a/Framework/Kernel/inc/MantidKernel/CrashService.h b/Framework/Kernel/inc/MantidKernel/CrashService.h new file mode 100644 index 0000000000000000000000000000000000000000..d8f89e20d97f84e1abc5eb97bbf1fff0341ef5ff --- /dev/null +++ b/Framework/Kernel/inc/MantidKernel/CrashService.h @@ -0,0 +1,56 @@ +#ifndef MANTID_KERNEL_CRASHSERVICE_H_ +#define MANTID_KERNEL_CRASHSERVICE_H_ + +#include <string> +#include <Poco/ActiveMethod.h> + +#include "MantidKernel/DllConfig.h" + +namespace Mantid { +namespace Kernel { + +/** CrashReporter : The Crash reporter is responsible for sending crash reports + + Copyright © 2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ + +class MANTID_KERNEL_DLL CrashServiceImpl { + public: + CrashServiceImpl(std::string application); + void sendCrashReport(); + + protected: + virtual std::string generateCrashMessage(); + virtual int sendReport(const std::string &message, + const std::string &url); + + private: + int sendCrashAsyncImpl(const std::string &message); + const std::string m_application; + /// Async method for sending startup notifications + Poco::ActiveMethod<int, std::string, CrashServiceImpl> m_crashActiveMethod; +}; + +} // namespace Kernel +} // namespace Mantid + +#endif /* MANTID_KERNEL_CRASHSERVICE_H_ */ \ No newline at end of file diff --git a/Framework/Kernel/src/CrashService.cpp b/Framework/Kernel/src/CrashService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..54023d148bb61ff499f9dbba9bf09d97f8d4c062 --- /dev/null +++ b/Framework/Kernel/src/CrashService.cpp @@ -0,0 +1,107 @@ +#include "MantidKernel/CrashService.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 "MantidKernel/FacilityInfo.h" + +#include <Poco/ActiveResult.h> + +#include <json/json.h> + +namespace Mantid { +namespace Kernel { + +namespace { +/// static logger +Logger g_log("CrashServiceImpl"); +} +const std::string CRASH_URL("http://crashreports.mantidproject.org/api/crash"); +// const std::string STARTUP_URL( +// "http://posttestserver.com/post.php?dir=Mantid"); // dev location +// http://posttestserver.com/data/ + +//---------------------------------------------------------------------------------------------- +// Constructor for CrashServiceImpl +CrashServiceImpl::CrashServiceImpl(std::string application) + : m_application(application), m_crashActiveMethod(this, &CrashServiceImpl::sendCrashAsyncImpl) { + +} + +void CrashServiceImpl::sendCrashReport() { + try { + std::string message = this->generateCrashMessage(); + + // send the report + Poco::ActiveResult<int> result = m_crashActiveMethod(message); + } catch (std::exception &ex) { + g_log.debug() << "Send crash report failure. " << ex.what() << '\n'; + } +} + +std::string CrashServiceImpl::generateCrashMessage() { + ::Json::Value message; + + // username + message["uid"] = Kernel::ChecksumHelper::md5FromString( + ConfigService::Instance().getUsername()); + // hostname + message["host"] = Kernel::ChecksumHelper::md5FromString( + ConfigService::Instance().getComputerName()); + + // os name, version, and architecture + message["osName"] = ConfigService::Instance().getOSName(); + message["osArch"] = ConfigService::Instance().getOSArchitecture(); + message["osVersion"] = ConfigService::Instance().getOSVersion(); + message["osReadable"] = ConfigService::Instance().getOSVersionReadable(); + + #if defined(MAKE_VATES) + // paraview + message["ParaView"] = Kernel::ParaViewVersion::targetVersion(); + #else + message["ParaView"] = 0; + #endif + + // mantid version and sha1 + message["mantidVersion"] = MantidVersion::version(); + message["mantidSha1"] = MantidVersion::revisionFull(); + + message["dateTime"] = + Types::Core::DateAndTime::getCurrentTime().toISO8601String(); + + message["application"] = m_application; + + message["facility"] = ConfigService::Instance().getFacility().name(); + + ::Json::FastWriter writer; + return writer.write(message); +} + +int CrashServiceImpl::sendCrashAsyncImpl(const std::string &message) { + return this->sendReport(message, CRASH_URL); +} + +int CrashServiceImpl::sendReport(const std::string &message, + const std::string &url) { + int status = -1; + try { + Kernel::InternetHelper helper; + std::stringstream responseStream; + helper.setTimeout(20); + 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 Kernel +} // namespace Mantid \ No newline at end of file diff --git a/Framework/Kernel/test/CrashServiceTest.h b/Framework/Kernel/test/CrashServiceTest.h new file mode 100644 index 0000000000000000000000000000000000000000..92e894a3081f1d87647b59eb929bd669cf700bd4 --- /dev/null +++ b/Framework/Kernel/test/CrashServiceTest.h @@ -0,0 +1,61 @@ +#ifndef MANTID_API_CRASHSERVICETEST_H_ +#define MANTID_API_CRASHSERVICETEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidKernel/CrashService.h" +#include <algorithm> +#include <json/json.h> + +using Mantid::Kernel::CrashServiceImpl; + +class TestableCrashService : public CrashServiceImpl { +public: + //TestableCrashService() : CrashServiceImpl() {} + using CrashServiceImpl::CrashServiceImpl; + + /// generates the message body for a crash message + std::string generateCrashMessage() override { + return CrashServiceImpl::generateCrashMessage(); + } + +protected: + /// sends a report over the internet + int sendReport(const std::string &message, const std::string &url) override { + UNUSED_ARG(message); + UNUSED_ARG(url); + // do nothing + return 200; + } +}; + +class CrashServiceTest : public CxxTest::TestSuite { +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static CrashServiceTest *createSuite() { return new CrashServiceTest(); } + static void destroySuite(CrashServiceTest *suite) { delete suite; } + + void test_crashMessage() { + std::string name = "My testing application name"; + TestableCrashService crashService(name); + std::string message = crashService.generateCrashMessage(); + + ::Json::Reader reader; + ::Json::Value root; + reader.parse(message, root); + auto members = root.getMemberNames(); + std::vector<std::string> expectedMembers{ + "ParaView", "application", "host", "mantidSha1", "mantidVersion", + "osArch", "osName", "osReadable", "osVersion", "uid", "facility"}; + for (auto expectedMember : expectedMembers) { + TSM_ASSERT(expectedMember + " not found", + std::find(members.begin(), members.end(), expectedMember) != + members.end()); + } + + TS_ASSERT_EQUALS(root["application"].asString(), name); + } +}; + +#endif /* MANTID_API_USAGESERVICETEST_H_ */ \ No newline at end of file diff --git a/MantidPlot/src/Mantid/MantidApplication.cpp b/MantidPlot/src/Mantid/MantidApplication.cpp index ddf25b1297ba10117776ba9dcbc8c02a145fbcda..15c9aec430ff783b226e4dd2deda160ddf0ad4f2 100644 --- a/MantidPlot/src/Mantid/MantidApplication.cpp +++ b/MantidPlot/src/Mantid/MantidApplication.cpp @@ -6,6 +6,7 @@ #include "MantidKernel/Logger.h" #include "MantidKernel/UsageService.h" +#include "MantidKernel/CrashService.h" #include <QMessageBox> #include <QPushButton> @@ -36,6 +37,14 @@ bool MantidApplication::notify(QObject *receiver, QEvent *event) { try { res = QApplication::notify(receiver, event); } catch (std::exception &e) { + + int reportingEnabled = 0; + int retVal = Mantid::Kernel::ConfigService::Instance().getValue("usagereports.enabled", reportingEnabled); + if (reportingEnabled && retVal == 1){ + Mantid::Kernel::CrashServiceImpl crashService("mantidplot"); + crashService.sendCrashReport(); + } + if (MantidQt::API::MantidDialog::handle(receiver, e)) return true; // stops event propagation