Include the log in crash reports
This commit is contained in:
parent
e1323e9cf4
commit
64bae947cc
|
@ -25,8 +25,11 @@
|
|||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
#include <QAbstractSocket>
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
#include <QLocalSocket>
|
||||
#include <QStringList>
|
||||
|
||||
#include <glib.h>
|
||||
|
@ -36,15 +39,32 @@
|
|||
|
||||
namespace logging {
|
||||
|
||||
static Level sDefaultLevel = Level_Debug;
|
||||
static QMap<QString, Level>* sClassLevels = NULL;
|
||||
static QIODevice* sNullDevice = NULL;
|
||||
|
||||
const char* kDefaultLogLevels = "GstEnginePipeline:2,*:3";
|
||||
|
||||
static const char* kMessageHandlerMagic = "__logging_message__";
|
||||
static const int kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
|
||||
static QtMsgHandler sOriginalMessageHandler = NULL;
|
||||
namespace {
|
||||
Level sDefaultLevel = Level_Debug;
|
||||
QMap<QString, Level>* sClassLevels = NULL;
|
||||
QIODevice* sNullDevice = NULL;
|
||||
|
||||
const char* kMessageHandlerMagic = "__logging_message__";
|
||||
const int kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
|
||||
QtMsgHandler sOriginalMessageHandler = NULL;
|
||||
|
||||
QList<QIODevice*> sOutputDevices;
|
||||
|
||||
// QIODevice doesn't have a flush() method, but its subclasses do. This picks
|
||||
// the right method to call depending on the runtime type of the device.
|
||||
void FlushDevice(QIODevice* device) {
|
||||
if (QFile* file = qobject_cast<QFile*>(device)) {
|
||||
file->flush();
|
||||
} else if (QAbstractSocket* socket = qobject_cast<QAbstractSocket*>(device)) {
|
||||
socket->flush();
|
||||
} else if (QLocalSocket* socket = qobject_cast<QLocalSocket*>(device)) {
|
||||
socket->flush();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void GLog(const char* domain, int level, const char* message, void* user_data) {
|
||||
|
@ -63,7 +83,16 @@ void GLog(const char* domain, int level, const char* message, void* user_data) {
|
|||
|
||||
static void MessageHandler(QtMsgType type, const char* message) {
|
||||
if (strncmp(kMessageHandlerMagic, message, kMessageHandlerMagicLength) == 0) {
|
||||
// Output to stderr.
|
||||
fprintf(stderr, "%s\n", message + kMessageHandlerMagicLength);
|
||||
|
||||
// Output to all the configured output devices.
|
||||
foreach (QIODevice* device, sOutputDevices) {
|
||||
device->write(message + kMessageHandlerMagicLength);
|
||||
device->write("\n", 1);
|
||||
FlushDevice(device);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -99,6 +128,10 @@ void Init() {
|
|||
}
|
||||
}
|
||||
|
||||
void AddOutputDevice(QIODevice* device) {
|
||||
sOutputDevices << device;
|
||||
}
|
||||
|
||||
void SetLevels(const QString& levels) {
|
||||
if (!sClassLevels)
|
||||
return;
|
||||
|
|
|
@ -48,6 +48,7 @@ namespace logging {
|
|||
|
||||
void Init();
|
||||
void SetLevels(const QString& levels);
|
||||
void AddOutputDevice(QIODevice* device);
|
||||
|
||||
void DumpStackTrace();
|
||||
|
||||
|
|
|
@ -24,17 +24,18 @@
|
|||
|
||||
const char* CrashReporting::kSendCrashReportOption = "--send-crash-report";
|
||||
char* CrashReporting::sPath = NULL;
|
||||
char* CrashReporting::sLogFilename = NULL;
|
||||
|
||||
|
||||
bool CrashReporting::SendCrashReport(int argc, char** argv) {
|
||||
#ifdef HAVE_BREAKPAD
|
||||
if (argc != 3 || strcmp(argv[1], kSendCrashReportOption) != 0) {
|
||||
if (argc != 4 || strcmp(argv[1], kSendCrashReportOption) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QApplication a(argc, argv);
|
||||
|
||||
CrashSender sender(argv[2]);
|
||||
CrashSender sender(argv[2], argv[3]);
|
||||
if (sender.Start()) {
|
||||
a.exec();
|
||||
}
|
||||
|
@ -48,3 +49,7 @@ bool CrashReporting::SendCrashReport(int argc, char** argv) {
|
|||
void CrashReporting::SetApplicationPath(const QString& path) {
|
||||
sPath = strdup(path.toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
void CrashReporting::SetLogFilename(const QString& path) {
|
||||
sLogFilename = strdup(path.toLocal8Bit().constData());
|
||||
}
|
||||
|
|
|
@ -46,15 +46,21 @@ public:
|
|||
// --send-crash-report when a crash happens.
|
||||
static void SetApplicationPath(const QString& path);
|
||||
|
||||
// If this is set then the contents of this file is sent along with any
|
||||
// crash report.
|
||||
static void SetLogFilename(const QString& path);
|
||||
|
||||
// Prints the message to stdout without using libc.
|
||||
static void Print(const char* message);
|
||||
|
||||
static const char* application_path() { return sPath; }
|
||||
static const char* log_filename() { return sLogFilename; }
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(CrashReporting);
|
||||
|
||||
static char* sPath;
|
||||
static char* sLogFilename;
|
||||
|
||||
boost::scoped_ptr<google_breakpad::ExceptionHandler> handler_;
|
||||
};
|
||||
|
|
|
@ -42,6 +42,7 @@ bool Handler(const google_breakpad::MinidumpDescriptor& dump,
|
|||
CrashReporting::application_path(),
|
||||
CrashReporting::kSendCrashReportOption,
|
||||
dump.path(),
|
||||
CrashReporting::log_filename(),
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "core/logging.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QCryptographicHash>
|
||||
#include <QFile>
|
||||
#include <QHttpMultiPart>
|
||||
#include <QMessageBox>
|
||||
|
@ -36,19 +35,28 @@
|
|||
const char* CrashSender::kUploadURL =
|
||||
"http://" CRASHREPORTING_HOSTNAME "/upload/crash";
|
||||
|
||||
CrashSender::CrashSender(const QString& path)
|
||||
CrashSender::CrashSender(const QString& minidump_filename,
|
||||
const QString& log_filename)
|
||||
: network_(new QNetworkAccessManager(this)),
|
||||
path_(path),
|
||||
file_(new QFile(path_, this)),
|
||||
minidump_filename_(minidump_filename),
|
||||
log_filename_(log_filename),
|
||||
minidump_(new QFile(minidump_filename_, this)),
|
||||
log_(new QFile(log_filename_, this)),
|
||||
progress_(NULL) {
|
||||
}
|
||||
|
||||
bool CrashSender::Start() {
|
||||
if (!file_->open(QIODevice::ReadOnly)) {
|
||||
qLog(Warning) << "Failed to open crash report" << path_;
|
||||
if (!minidump_->open(QIODevice::ReadOnly)) {
|
||||
qLog(Warning) << "Failed to open crash report" << minidump_filename_;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!log_filename_.isEmpty()) {
|
||||
if (!log_->open(QIODevice::ReadOnly)) {
|
||||
qLog(Warning) << "Failed to open log file" << log_filename_;
|
||||
}
|
||||
}
|
||||
|
||||
// No tr() here.
|
||||
QMessageBox prompt(QMessageBox::Warning, "Clementine has crashed!",
|
||||
"Clementine has crashed! A crash report has been created and saved to "
|
||||
|
@ -90,14 +98,23 @@ void CrashSender::RedirectFinished() {
|
|||
printf("Uploading crash report to %s\n", url.toEncoded().constData());
|
||||
QNetworkRequest req(url);
|
||||
|
||||
// Create the HTTP part for the crash report file
|
||||
QHttpPart part;
|
||||
part.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
"form-data; name=\"data\"; filename=\"data.dmp\"");
|
||||
part.setBodyDevice(file_);
|
||||
|
||||
QHttpMultiPart* multi_part = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||
multi_part->append(part);
|
||||
|
||||
// Create the HTTP part for the crash report file
|
||||
QHttpPart minidump_part;
|
||||
minidump_part.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
"form-data; name=\"data\"; filename=\"data.dmp\"");
|
||||
minidump_part.setBodyDevice(minidump_);
|
||||
multi_part->append(minidump_part);
|
||||
|
||||
// Create the HTTP part for the log file.
|
||||
if (log_->isOpen()) {
|
||||
QHttpPart log_part;
|
||||
log_part.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
"form-data; name=\"log\"; filename=\"log.txt\"");
|
||||
log_part.setBodyDevice(log_);
|
||||
multi_part->append(log_part);
|
||||
}
|
||||
|
||||
// Get some information about the thing that crashed and add that to the
|
||||
// request as well.
|
||||
|
@ -143,16 +160,6 @@ QList<CrashSender::ClientInfoPair> CrashSender::ClientInfo() const {
|
|||
ret.append(ClientInfoPair("version", CLEMENTINE_VERSION_DISPLAY));
|
||||
ret.append(ClientInfoPair("qt_version", qVersion()));
|
||||
|
||||
// Hash the binary
|
||||
QFile executable(QCoreApplication::applicationFilePath());
|
||||
if (executable.open(QIODevice::ReadOnly)) {
|
||||
QCryptographicHash hash(QCryptographicHash::Md5);
|
||||
while (!executable.atEnd()) {
|
||||
hash.addData(executable.read(4096));
|
||||
}
|
||||
ret.append(ClientInfoPair("exe_md5", hash.result().toHex()));
|
||||
}
|
||||
|
||||
// Get the OS version
|
||||
#if defined(Q_OS_MAC)
|
||||
ret.append(ClientInfoPair("os", "mac"));
|
||||
|
|
|
@ -33,7 +33,7 @@ class CrashSender : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CrashSender(const QString& path);
|
||||
CrashSender(const QString& minidump_filename, const QString& log_filename);
|
||||
|
||||
// Returns false if the user doesn't want to send the crash report (caller
|
||||
// should exit), or true if he does (caller should start the Qt event loop).
|
||||
|
@ -54,8 +54,10 @@ private:
|
|||
|
||||
QNetworkAccessManager* network_;
|
||||
|
||||
QString path_;
|
||||
QFile* file_;
|
||||
QString minidump_filename_;
|
||||
QString log_filename_;
|
||||
QFile* minidump_;
|
||||
QFile* log_;
|
||||
QProgressDialog* progress_;
|
||||
};
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QSettings>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
SomaFMUrlHandler::SomaFMUrlHandler(Application* app, SomaFMService* service,
|
||||
QObject* parent)
|
||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -60,6 +60,7 @@
|
|||
#include <QSslSocket>
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
#include <QTemporaryFile>
|
||||
#include <QTextCodec>
|
||||
#include <QTranslator>
|
||||
#include <QtConcurrentRun>
|
||||
|
@ -305,6 +306,15 @@ int main(int argc, char *argv[]) {
|
|||
logging::SetLevels(options.log_levels());
|
||||
g_log_set_default_handler(reinterpret_cast<GLogFunc>(&logging::GLog), NULL);
|
||||
|
||||
// Log to a temporary file that gets included with crash reports.
|
||||
QTemporaryFile temp_log(QDir::tempPath() + "/clementine-log-XXXXXX.txt");
|
||||
if (temp_log.open()) {
|
||||
logging::AddOutputDevice(&temp_log);
|
||||
CrashReporting::SetLogFilename(temp_log.fileName());
|
||||
|
||||
qLog(Info) << "Logging to" << temp_log.fileName();
|
||||
}
|
||||
|
||||
// Output the version, so when people attach log output to bug reports they
|
||||
// don't have to tell us which version they're using.
|
||||
qLog(Info) << "Clementine" << CLEMENTINE_VERSION_DISPLAY;
|
||||
|
|
Loading…
Reference in New Issue