Send crash reports to gist. Only Windows.

--HG--
branch : develop
This commit is contained in:
dismine 2014-11-15 17:52:27 +02:00
parent 776d8be8bb
commit 5abfea36bc
5 changed files with 207 additions and 48 deletions

View File

@ -34,6 +34,7 @@
#include "../exception/vexceptionwrongid.h" #include "../exception/vexceptionwrongid.h"
#include "vmaingraphicsview.h" #include "vmaingraphicsview.h"
#include "../container/calculator.h" #include "../container/calculator.h"
#include "../version.h"
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
@ -44,6 +45,7 @@
#include <QTemporaryFile> #include <QTemporaryFile>
const qreal VApplication::PrintDPI = 96.0; const qreal VApplication::PrintDPI = 96.0;
const QString VApplication::GistFileName = QStringLiteral("gist.json");
#define DefWidth 1.2//mm #define DefWidth 1.2//mm
@ -1848,3 +1850,152 @@ bool VApplication::SafeCopy(const QString &source, const QString &destination, Q
return result; return result;
} }
#if defined(Q_OS_WIN) && defined(Q_CC_GNU)
//---------------------------------------------------------------------------------------------------------------------
// Catch exception and create report. Use if program build with Mingw compiler.
// See more about catcher https://github.com/jrfonseca/drmingw/blob/master/README.md
void VApplication::DrMingw()
{
QFile drmingw("exchndl.dll");
if(drmingw.exists())
{// If don't want create reports just delete exchndl.dll from installer
LoadLibrary(L"exchndl.dll");
}
}
//---------------------------------------------------------------------------------------------------------------------
void VApplication::CollectReports() const
{
// Seek file "binary_name.RPT"
const QString reportName = QString("%1/%2.RPT").arg(applicationDirPath())
.arg(QFileInfo(arguments().at(0)).baseName());
QFile reportFile(reportName);
if (reportFile.exists())
{ // Hooray we have found crash
if (settings == nullptr)
{
return;// Settings was not opened.
}
if (settings->value("configuration/send_report/state", 1).toBool())
{ // Try send report
// Remove gist.json file before close app.
connect(this, &VApplication::aboutToQuit, this, &VApplication::CleanGist, Qt::UniqueConnection);
SendReport(reportName);
}
else
{ // Just collect report to /reports directory
CollectReport(reportName);
}
}
}
//---------------------------------------------------------------------------------------------------------------------
void VApplication::CollectReport(const QString &reportName) const
{
const QString reportsDir = QString("%1/reports").arg(qApp->applicationDirPath());
QDir reports(reportsDir);
if (reports.exists() == false)
{
reports.mkpath("."); // Create directory for reports if need
}
const QDateTime now = QDateTime::currentDateTime();
const QString timestamp = now.toString(QLatin1String("yyyyMMdd-hhmmsszzz"));
const QString filename = QString("%1/reports/crash-%2.RPT").arg(qApp->applicationDirPath()).arg(timestamp);
QFile reportFile(reportName);
reportFile.copy(filename); // Collect new crash
reportFile.remove(); // Clear after yourself
}
//---------------------------------------------------------------------------------------------------------------------
void VApplication::CleanGist() const
{
QFile gistFile(GistFileName);
if (gistFile.exists())
{
gistFile.remove();
}
}
//---------------------------------------------------------------------------------------------------------------------
void VApplication::SendReport(const QString &reportName) const
{
QString content;
QFile reportFile(reportName);
if (!reportFile.open(QIODevice::ReadOnly | QIODevice::Text))
{
return;
}
QTextStream in(&reportFile);
while (!in.atEnd())
{
content.append(in.readLine()+"\r\n");// Windows end of line
}
reportFile.close();
// Additional information
content.append(QString("-------------------------------")+"\r\n");
content.append(QString("Version:%1").arg(APP_VERSION)+"\r\n");
content.append(QString("Based on Qt %2 (32 bit)").arg(QT_VERSION_STR)+"\r\n");
content.append(QString("Built on %3 at %4").arg(__DATE__).arg(__TIME__)+"\r\n");
// Creating json with report
// Example:
//{
// "description":"Crash report",
// "public":"true",
// "files":{
// "file1.txt":{
// "content":"Report text here"
// }
// }
//}
// Useful to know when crash was created
const QDateTime now = QDateTime::currentDateTime();
const QString timestamp = now.toString(QLatin1String("yyyy/MM/dd hh:mm:ss:zzz"));
const QString report = QString("Crash report was created %2").arg(timestamp);
QJsonObject reportObject;
reportObject.insert(QStringLiteral("description"), QJsonValue(report));
reportObject.insert(QStringLiteral("public"), QJsonValue(QString("true")));
QJsonObject contentObject;
contentObject.insert(QStringLiteral("content"), QJsonValue(content));
QJsonObject fileObject;
fileObject.insert(QFileInfo(reportName).fileName(), QJsonValue(contentObject));
reportObject.insert(QStringLiteral("files"), QJsonValue(fileObject));
QFile gistFile(GistFileName);
if (!gistFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
{
qDebug("Couldn't open gist file.");
return;
}
// Save data to file
QJsonDocument saveRep(reportObject);
gistFile.write(saveRep.toJson());
gistFile.close();
QFile curlFile("curl.exe");
if (curlFile.exists())
{// Trying send report
// Change token 28df778e0ef75e3724f7b9622fb70b9c69187779 if need
QString arg = QString("curl.exe -k -H \"Authorization: bearer 28df778e0ef75e3724f7b9622fb70b9c69187779\" "
"-H \"Accept: application/json\" -H \"Content-type: application/json\" -X POST "
"--data @gist.json https://api.github.com/gists");
QProcess::startDetached(arg);
reportFile.remove();// Clear after yourself
}
else
{// We can not send than just collect
CollectReport(reportName);
}
}
#endif //defined(Q_OS_WIN) && defined(Q_CC_GNU)

View File

@ -103,6 +103,13 @@ public:
static QStringList LabelLanguages(); static QStringList LabelLanguages();
QString STDescription(const QString &id)const; QString STDescription(const QString &id)const;
static bool SafeCopy(const QString &source, const QString &destination, QString &error); static bool SafeCopy(const QString &source, const QString &destination, QString &error);
#if defined(Q_OS_WIN) && defined(Q_CC_GNU)
static void DrMingw();
void CollectReports() const;
private slots:
void CleanGist() const;
#endif
private: private:
Q_DISABLE_COPY(VApplication) Q_DISABLE_COPY(VApplication)
Unit _patternUnit; Unit _patternUnit;
@ -153,6 +160,13 @@ private:
void BiasTokens(int position, int bias, QMap<int, QString> &tokens) const; void BiasTokens(int position, int bias, QMap<int, QString> &tokens) const;
void InitMeasurement(const QString &name, const VTranslation &m, const VTranslation &g, void InitMeasurement(const QString &name, const VTranslation &m, const VTranslation &g,
const VTranslation &d); const VTranslation &d);
#if defined(Q_OS_WIN) && defined(Q_CC_GNU)
static const QString GistFileName;
void CollectReport(const QString &reportName) const;
void SendReport(const QString &reportName) const;
#endif
}; };
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------

View File

@ -44,14 +44,17 @@
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
ConfigurationPage::ConfigurationPage(QWidget *parent) ConfigurationPage::ConfigurationPage(QWidget *parent)
: QWidget(parent), autoSaveCheck(nullptr), autoTime(nullptr), langCombo(nullptr), labelCombo(nullptr), : QWidget(parent), autoSaveCheck(nullptr), autoTime(nullptr), langCombo(nullptr), labelCombo(nullptr),
unitCombo(nullptr), osOptionCheck(nullptr), langChanged(false), unitChanged(false), labelLangChanged(false) unitCombo(nullptr), osOptionCheck(nullptr), langChanged(false), unitChanged(false), labelLangChanged(false),
sendReportCheck(nullptr)
{ {
QGroupBox *saveGroup = SaveGroup(); QGroupBox *saveGroup = SaveGroup();
QGroupBox *langGroup = LangGroup(); QGroupBox *langGroup = LangGroup();
QGroupBox *sendGroup = SendGroup();
QVBoxLayout *mainLayout = new QVBoxLayout; QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(saveGroup); mainLayout->addWidget(saveGroup);
mainLayout->addWidget(langGroup); mainLayout->addWidget(langGroup);
mainLayout->addWidget(sendGroup);
mainLayout->addStretch(1); mainLayout->addStretch(1);
setLayout(mainLayout); setLayout(mainLayout);
} }
@ -76,6 +79,8 @@ void ConfigurationPage::Apply()
qApp->getSettings()->setValue("configuration/osSeparator", osOptionCheck->isChecked()); qApp->getSettings()->setValue("configuration/osSeparator", osOptionCheck->isChecked());
qApp->getSettings()->setValue("configuration/send_report/state", sendReportCheck->isChecked());
if (langChanged) if (langChanged)
{ {
QString locale = qvariant_cast<QString>(langCombo->itemData(langCombo->currentIndex())); QString locale = qvariant_cast<QString>(langCombo->itemData(langCombo->currentIndex()));
@ -273,6 +278,35 @@ QGroupBox *ConfigurationPage::LangGroup()
return langGroup; return langGroup;
} }
//---------------------------------------------------------------------------------------------------------------------
QGroupBox *ConfigurationPage::SendGroup()
{
QSettings *settings = qApp->getSettings();
SCASSERT(settings != nullptr);
QGroupBox *sendGroup = new QGroupBox(tr("Send crash reports"));
sendReportCheck = new QCheckBox(tr("Send crash reports (recommended)"));
bool sendReportValue = settings->value("configuration/send_report/state", 1).toBool();
sendReportCheck->setChecked(sendReportValue);
QLabel *description = new QLabel(tr("After each crash Valentina collect information that may help us fix a "
"problem. We do not collect any personal information. Find more about what "
"<a href=\"https://bitbucket.org/dismine/valentina/wiki/manual/"
"Crash_reports\">kind of information</a> we collect."));
description->setTextFormat(Qt::RichText);
description->setTextInteractionFlags(Qt::TextBrowserInteraction);
description->setOpenExternalLinks(true);
description->setWordWrap(true);
QVBoxLayout *sendLayout = new QVBoxLayout;
sendLayout->addWidget(sendReportCheck);
sendLayout->addWidget(description);
sendGroup->setLayout(sendLayout);
return sendGroup;
}
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
void ConfigurationPage::SetLabelComboBox(const QStringList &list) void ConfigurationPage::SetLabelComboBox(const QStringList &list)
{ {

View File

@ -58,9 +58,11 @@ private:
bool langChanged; bool langChanged;
bool unitChanged; bool unitChanged;
bool labelLangChanged; bool labelLangChanged;
QCheckBox *sendReportCheck;
QGroupBox *SaveGroup(); QGroupBox *SaveGroup();
QGroupBox *LangGroup(); QGroupBox *LangGroup();
QGroupBox *SendGroup();
void SetLabelComboBox(const QStringList &list); void SetLabelComboBox(const QStringList &list);
}; };

View File

@ -122,48 +122,6 @@ inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &con
} }
} }
//---------------------------------------------------------------------------------------------------------------------
// Catch exception and create report. Use if program build with Mingw compiler.
// See more about catcher https://github.com/jrfonseca/drmingw/blob/master/README.md
void DrMingw()
{
#if defined(Q_OS_WIN) && defined(Q_CC_GNU)
// Put exchndl.dll near binary file
QFile drmingw("exchndl.dll");
if(drmingw.exists())
{// If don't want create reports just delete exchndl.dll from installer
LoadLibrary(L"exchndl.dll");
}
#endif
}
//---------------------------------------------------------------------------------------------------------------------
// Check if last run was crash.
// Each new crash we will lose old crashes reports if don't move them in save place.
// See more about catcher https://github.com/jrfonseca/drmingw/blob/master/README.md
void CollectReport()
{
// Seek file "binary_name.RPT"
const QString reportName = QString("%1/%2.RPT").arg(qApp->applicationDirPath())
.arg(QFileInfo(qApp->arguments().at(0)).baseName());
QFile reportFile(reportName);
if (reportFile.exists())
{ // Hooray we have found crash
const QString reportsDir = QString("%1/reports").arg(qApp->applicationDirPath());
QDir reports(reportsDir);
if (reports.exists() == false)
{
reports.mkpath("."); // Create directory for reports if need
}
const QDateTime now = QDateTime::currentDateTime();
const QString timestamp = now.toString(QLatin1String("yyyyMMdd-hhmmsszzz"));
const QString filename = QString("%1/reports/crash-%2.RPT").arg(qApp->applicationDirPath()).arg(timestamp);
reportFile.copy(filename); // Collect new crash
reportFile.remove(); // Clear after yourself
}
}
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@ -176,11 +134,6 @@ int main(int argc, char *argv[])
VApplication app(argc, argv); VApplication app(argc, argv);
#if defined(Q_OS_WIN) && defined(Q_CC_GNU)
DrMingw();
CollectReport();
#endif
#ifdef QT_DEBUG #ifdef QT_DEBUG
// Because our "noisy" message handler uses the GUI subsystem for message // Because our "noisy" message handler uses the GUI subsystem for message
// boxes, we can't install it until after the QApplication is constructed. But it // boxes, we can't install it until after the QApplication is constructed. But it
@ -198,6 +151,11 @@ int main(int argc, char *argv[])
app.OpenSettings(); app.OpenSettings();
#if defined(Q_OS_WIN) && defined(Q_CC_GNU)
VApplication::DrMingw();
app.CollectReports();
#endif
QString checkedLocale = qApp->getSettings()->value("configuration/locale", QLocale::system().name()).toString(); QString checkedLocale = qApp->getSettings()->value("configuration/locale", QLocale::system().name()).toString();
QTranslator qtTranslator; QTranslator qtTranslator;