Save log to file. Send or collect after crash (only Windows).

--HG--
branch : develop
This commit is contained in:
dismine 2014-11-20 14:18:43 +02:00
parent 399706237d
commit 8c908f84ee
3 changed files with 216 additions and 103 deletions

View File

@ -43,6 +43,97 @@
#include <QUndoStack>
#include <QtCore/qmath.h>
#include <QTemporaryFile>
#include <QFile>
#include <QStandardPaths>
#include <QMessageBox>
//---------------------------------------------------------------------------------------------------------------------
inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
// Why on earth didn't Qt want to make failed signal/slot connections qWarning?
if ((type == QtDebugMsg) && msg.contains("::connect"))
{
type = QtWarningMsg;
}
// this is another one that doesn't make sense as just a debug message. pretty serious
// sign of a problem
// http://www.developer.nokia.com/Community/Wiki/QPainter::begin:Paint_device_returned_engine_%3D%3D_0_(Known_Issue)
if ((type == QtDebugMsg) && msg.contains("QPainter::begin") && msg.contains("Paint device returned engine"))
{
type = QtWarningMsg;
}
// This qWarning about "Cowardly refusing to send clipboard message to hung application..."
// is something that can easily happen if you are debugging and the application is paused.
// As it is so common, not worth popping up a dialog.
if ((type == QtWarningMsg) && QString(msg).contains("QClipboard::event")
&& QString(msg).contains("Cowardly refusing"))
{
type = QtDebugMsg;
}
// only the GUI thread should display message boxes. If you are
// writing a multithreaded application and the error happens on
// a non-GUI thread, you'll have to queue the message to the GUI
QCoreApplication *instance = QCoreApplication::instance();
const bool isGuiThread = instance && (QThread::currentThread() == instance->thread());
if (isGuiThread)
{
QString debugdate = QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss");
QMessageBox messageBox;
switch (type)
{
case QtDebugMsg:
debugdate += QString(" [Debug] %1: %2 (%3:%4, %5)").arg(context.category).arg(msg).arg(context.file)
.arg(context.line).arg(context.function);
break;
case QtWarningMsg:
debugdate += QString(" [Warning] %1: %2 (%3:%4, %5)").arg(context.category).arg(msg).arg(context.file)
.arg(context.line).arg(context.function);
messageBox.setIcon(QMessageBox::Warning);
messageBox.setInformativeText(msg);
messageBox.setStandardButtons(QMessageBox::Ok);
messageBox.exec();
break;
case QtCriticalMsg:
debugdate += QString(" [Critical] %1: %2 (%3:%4, %5)").arg(context.category).arg(msg).arg(context.file)
.arg(context.line).arg(context.function);
messageBox.setIcon(QMessageBox::Critical);
messageBox.setInformativeText(msg);
messageBox.setStandardButtons(QMessageBox::Ok);
messageBox.exec();
break;
case QtFatalMsg:
debugdate += QString(" [Fatal] %1: %2 (%3:%4, %5)").arg(context.category).arg(msg).arg(context.file)
.arg(context.line).arg(context.function);
messageBox.setIcon(QMessageBox::Critical);
messageBox.setInformativeText(msg);
messageBox.setStandardButtons(QMessageBox::Ok);
messageBox.exec();
break;
default:
break;
}
(*qApp->LogFile()) << debugdate << endl;
if (QtFatalMsg == type)
{
abort();
}
}
else
{
if (type != QtDebugMsg)
{
abort(); // be NOISY unless overridden!
}
}
}
//---------------------------------------------------------------------------------------------------------------------
const qreal VApplication::PrintDPI = 96.0;
@ -64,8 +155,8 @@ VApplication::VApplication(int &argc, char **argv)
guiTexts(QMap<QString, VTranslation>()), descriptions(QMap<QString, VTranslation>()),
variables(QMap<QString, VTranslation>()), functions(QMap<QString, VTranslation>()),
postfixOperators(QMap<QString, VTranslation>()), stDescriptions(QMap<QString, VTranslation>()),
undoStack(nullptr), sceneView(nullptr), currentScene(nullptr),
autoSaveTimer(nullptr), mainWindow(nullptr), openingPattern(false), settings(nullptr), doc(nullptr)
undoStack(nullptr), sceneView(nullptr), currentScene(nullptr), autoSaveTimer(nullptr), mainWindow(nullptr),
openingPattern(false), settings(nullptr), doc(nullptr), log(nullptr), out(nullptr)
{
undoStack = new QUndoStack(this);
@ -77,6 +168,19 @@ VApplication::VApplication(int &argc, char **argv)
InitSTDescriptions();
}
//---------------------------------------------------------------------------------------------------------------------
VApplication::~VApplication()
{
qInstallMessageHandler(0); // Resore the message handler
delete out;
if (log != nullptr)
{
log->close();
delete log;
}
}
//---------------------------------------------------------------------------------------------------------------------
/**
* @brief NewValentina start Valentina in new process, send path to pattern file in argument.
@ -295,6 +399,25 @@ void VApplication::InitMeasurement(const QString &name, const VTranslation &m, c
descriptions.insert(name, d);
}
//---------------------------------------------------------------------------------------------------------------------
QString VApplication::LogDirPath() const
{
#if defined(Q_OS_WIN) || defined(Q_OS_OSX)
const QString logDirPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString(),
QStandardPaths::LocateDirectory) + "Valentina";
#else
const QString logDirPath = QStandardPaths::locate(QStandardPaths::ConfigLocation, QString(),
QStandardPaths::LocateDirectory) + organizationName();
#endif
return logDirPath;
}
//---------------------------------------------------------------------------------------------------------------------
QString VApplication::LogPath() const
{
return LogDirPath() + "/valentina.log";
}
//---------------------------------------------------------------------------------------------------------------------
void VApplication::InitMeasurements()
{
@ -1854,6 +1977,34 @@ bool VApplication::SafeCopy(const QString &source, const QString &destination, Q
return result;
}
//---------------------------------------------------------------------------------------------------------------------
void VApplication::StartLogging()
{
QDir logDir(LogDirPath());
if (logDir.exists() == false)
{
logDir.mkpath("."); // Create directory for log if need
}
log = new QFile(LogPath());
if (log->open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
{
out = new QTextStream(log);
qInstallMessageHandler(noisyFailureMsgHandler);
}
else
{
delete log;
qDebug() << "Error opening log file '" << LogPath() << "'. All debug output redirected to console.";
}
}
//---------------------------------------------------------------------------------------------------------------------
QTextStream *VApplication::LogFile()
{
return out;
}
#if defined(Q_OS_WIN) && defined(Q_CC_GNU)
//---------------------------------------------------------------------------------------------------------------------
// Catch exception and create report. Use if program build with Mingw compiler.
@ -1905,12 +2056,16 @@ void VApplication::CollectReport(const QString &reportName) const
}
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);
const QString timestamp = now.toString(QLatin1String("yyyy.MM.dd-hh_mm_ss"));
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
filename = QString("%1/reports/log-%2.log").arg(qApp->applicationDirPath()).arg(timestamp);
QFile logFile(LogPath());
logFile.copy(filename); // Collect log
}
//---------------------------------------------------------------------------------------------------------------------
@ -1928,23 +2083,22 @@ void VApplication::SendReport(const QString &reportName) const
{
QString content;
QFile reportFile(reportName);
if (!reportFile.open(QIODevice::ReadOnly | QIODevice::Text))
if (reportFile.open(QIODevice::ReadOnly | QIODevice::Text))
{
return;
}
QTextStream in(&reportFile);
while (!in.atEnd())
{
content.append(in.readLine()+"\r\n");// Windows end of line
}
content = ReadFileForSending(reportFile);
reportFile.close();
}
else
{
content = reportFile.errorString() + "\r\n";
}
// 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");
content.append("\r\n");
// Creating json with report
// Example:
@ -1952,7 +2106,7 @@ void VApplication::SendReport(const QString &reportName) const
// "description":"Crash report",
// "public":"true",
// "files":{
// "file1.txt":{
// "valentina.RPT":{
// "content":"Report text here"
// }
// }
@ -1967,12 +2121,29 @@ void VApplication::SendReport(const QString &reportName) const
reportObject.insert(QStringLiteral("description"), QJsonValue(report));
reportObject.insert(QStringLiteral("public"), QJsonValue(QString("true")));
QJsonObject contentObject;
contentObject.insert(QStringLiteral("content"), QJsonValue(content));
content.append("\r\n");
content.append(QString("-------------------------------")+"\r\n");
content.append(QString("Log:")+"\r\n");
QFile logFile(LogPath());
if (logFile.open(QIODevice::ReadOnly | QIODevice::Text))
{
content.append(ReadFileForSending(logFile));
logFile.close();
}
else
{
content = logFile.errorString() + "\r\n";
}
const QString contentSection = QStringLiteral("content");
QJsonObject contentObject;
contentObject.insert(contentSection, QJsonValue(content));
const QString filesSection = QStringLiteral("files");
QJsonObject fileObject;
fileObject.insert(QFileInfo(reportName).fileName(), QJsonValue(contentObject));
reportObject.insert(QStringLiteral("files"), QJsonValue(fileObject));
reportObject.insert(filesSection, QJsonValue(fileObject));
QFile gistFile(GistFileName);
if (!gistFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
@ -2001,4 +2172,16 @@ void VApplication::SendReport(const QString &reportName) const
CollectReport(reportName);
}
}
//---------------------------------------------------------------------------------------------------------------------
QString VApplication::ReadFileForSending(QFile &file) const
{
QString content;
QTextStream in(&file);
while (!in.atEnd())
{
content.append(in.readLine()+"\r\n");// Windows end of line
}
return content;
}
#endif //defined(Q_OS_WIN) && defined(Q_CC_GNU)

View File

@ -40,6 +40,7 @@ class QUndoStack;
class VMainGraphicsView;
class VMainGraphicsScene;
class VPattern;
class QFile;
#if defined(qApp)
#undef qApp
@ -54,7 +55,7 @@ class VApplication : public QApplication
Q_OBJECT
public:
VApplication(int &argc, char ** argv);
virtual ~VApplication() {}
virtual ~VApplication();
static void NewValentina(const QString &fileName = QString());
static void CheckFactor(qreal &oldFactor, const qreal &Newfactor);
virtual bool notify(QObject * receiver, QEvent * event);
@ -103,13 +104,19 @@ public:
static QStringList LabelLanguages();
QString STDescription(const QString &id)const;
static bool SafeCopy(const QString &source, const QString &destination, QString &error);
void StartLogging();
QTextStream *LogFile();
#if defined(Q_OS_WIN) && defined(Q_CC_GNU)
static void DrMingw();
void CollectReports() const;
#endif // defined(Q_OS_WIN) && defined(Q_CC_GNU)
private slots:
#if defined(Q_OS_WIN) && defined(Q_CC_GNU)
void CleanGist() const;
#endif // defined(Q_OS_WIN) && defined(Q_CC_GNU)
private:
Q_DISABLE_COPY(VApplication)
Unit _patternUnit;
@ -143,6 +150,8 @@ private:
QSettings *settings;
VPattern *doc;
QFile *log;
QTextStream *out;
void InitLineWidth();
void InitMeasurements();
void InitVariables();
@ -166,7 +175,11 @@ private:
void CollectReport(const QString &reportName) const;
void SendReport(const QString &reportName) const;
QString ReadFileForSending(QFile &file)const;
#endif // defined(Q_OS_WIN) && defined(Q_CC_GNU)
QString LogDirPath()const;
QString LogPath()const;
};
//---------------------------------------------------------------------------------------------------------------------

View File

@ -43,85 +43,6 @@
#include "tablewindow.h"
#include "version.h"
//---------------------------------------------------------------------------------------------------------------------
inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
// Why on earth didn't Qt want to make failed signal/slot connections qWarning?
if ((type == QtDebugMsg) && msg.contains("::connect"))
{
type = QtWarningMsg;
}
// this is another one that doesn't make sense as just a debug message. pretty serious
// sign of a problem
// http://www.developer.nokia.com/Community/Wiki/QPainter::begin:Paint_device_returned_engine_%3D%3D_0_(Known_Issue)
if ((type == QtDebugMsg) && msg.contains("QPainter::begin") && msg.contains("Paint device returned engine"))
{
type = QtWarningMsg;
}
// This qWarning about "Cowardly refusing to send clipboard message to hung application..."
// is something that can easily happen if you are debugging and the application is paused.
// As it is so common, not worth popping up a dialog.
if ((type == QtWarningMsg) && QString(msg).contains("QClipboard::event")
&& QString(msg).contains("Cowardly refusing"))
{
type = QtDebugMsg;
}
// only the GUI thread should display message boxes. If you are
// writing a multithreaded application and the error happens on
// a non-GUI thread, you'll have to queue the message to the GUI
QCoreApplication *instance = QCoreApplication::instance();
const bool isGuiThread = instance && (QThread::currentThread() == instance->thread());
if (isGuiThread)
{
QByteArray localMsg = msg.toLocal8Bit();
QMessageBox messageBox;
switch (type)
{
case QtDebugMsg:
fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line,
context.function);
return;
case QtWarningMsg:
messageBox.setIcon(QMessageBox::Warning);
messageBox.setInformativeText(msg);
messageBox.setStandardButtons(QMessageBox::Ok);
fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line,
context.function);
messageBox.exec();
break;
case QtCriticalMsg:
messageBox.setIcon(QMessageBox::Critical);
messageBox.setInformativeText(msg);
messageBox.setStandardButtons(QMessageBox::Ok);
fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line,
context.function);
messageBox.exec();
abort();
case QtFatalMsg:
messageBox.setIcon(QMessageBox::Critical);
messageBox.setInformativeText(msg);
messageBox.setStandardButtons(QMessageBox::Ok);
fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line,
context.function);
messageBox.exec();
abort();
default:
break;
}
}
else
{
if (type != QtDebugMsg)
{
abort(); // be NOISY unless overridden!
}
}
}
//---------------------------------------------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
@ -134,14 +55,6 @@ int main(int argc, char *argv[])
VApplication app(argc, argv);
#ifdef QT_DEBUG
// 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
// is good to be the very next thing to run, to start catching warnings ASAP.
{
qInstallMessageHandler(noisyFailureMsgHandler);
}
#endif
app.setApplicationDisplayName(VER_PRODUCTNAME_STR);
app.setApplicationName(VER_INTERNALNAME_STR);
app.setOrganizationName(VER_COMPANYNAME_STR);
@ -152,10 +65,14 @@ int main(int argc, char *argv[])
app.OpenSettings();
#if defined(Q_OS_WIN) && defined(Q_CC_GNU)
// Catch and send report
VApplication::DrMingw();
app.CollectReports();
#endif
// Run creation log after sending crash report
app.StartLogging();
QString checkedLocale = qApp->getSettings()->value("configuration/locale", QLocale::system().name()).toString();
QTranslator qtTranslator;