From 443d709eb8457be3f90def1154ca573c7651e491 Mon Sep 17 00:00:00 2001 From: Tobias Fella Date: Fri, 5 May 2023 10:15:43 +0000 Subject: [PATCH] Save log to file and always save e2ee debug logs This will hopefully help us debug e2ee problems --- src/CMakeLists.txt | 1 + src/logger.cpp | 208 +++++++++++++++++++++++++++++++++++++++++++++ src/logger.h | 9 ++ src/main.cpp | 3 + 4 files changed, 221 insertions(+) create mode 100644 src/logger.cpp create mode 100644 src/logger.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index be65742ff..3ebed42d8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,6 +48,7 @@ add_library(neochat STATIC filetransferpseudojob.cpp models/searchmodel.cpp texthandler.cpp + logger.cpp ) add_executable(neochat-app diff --git a/src/logger.cpp b/src/logger.cpp new file mode 100644 index 000000000..942023339 --- /dev/null +++ b/src/logger.cpp @@ -0,0 +1,208 @@ +// SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer +// SPDX-FileCopyrightText: 2002 Holger Freyther +// SPDX-FileCopyrightText: 2008 Volker Krause +// SPDX-FileCopyrightText: 2023 Tobias Fella +// SPDX-License-Identifier: LGPL-2.0-or-later + +#include "logger.h" + +#include +#include +#include +#include +#include +#include + +static QLoggingCategory::CategoryFilter oldCategoryFilter = nullptr; +static QtMessageHandler oldHandler = nullptr; +static bool e2eeDebugEnabled = false; + +class FileDebugStream : public QIODevice +{ + Q_OBJECT +public: + FileDebugStream() + : mType(QtCriticalMsg) + { + open(WriteOnly); + } + + bool isSequential() const override + { + return true; + } + qint64 readData(char *, qint64) override + { + return 0; + } + qint64 readLineData(char *, qint64) override + { + return 0; + } + + qint64 writeData(const char *data, qint64 len) override + { + if (!mFileName.isEmpty()) { + QFile outputFile(mFileName); + outputFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Unbuffered); + outputFile.write(data, len); + outputFile.putChar('\n'); + outputFile.close(); + } + + return len; + } + + void setFileName(const QString &fileName) + { + mFileName = fileName; + } + + void setType(QtMsgType type) + { + mType = type; + } + +private: + QString mFileName; + QtMsgType mType; +}; + +class DebugPrivate +{ +public: + DebugPrivate() + : origHandler(nullptr) + { + } + + ~DebugPrivate() + { + qInstallMessageHandler(origHandler); + file.close(); + } + + void log(QtMsgType type, const QMessageLogContext &context, const QString &message) + { + QMutexLocker locker(&mutex); + QByteArray buf; + QTextStream str(&buf); + str << QDateTime::currentDateTime().toString(Qt::ISODate) << " ["; + switch (type) { + case QtDebugMsg: + str << "DEBUG"; + break; + case QtInfoMsg: + str << "INFO "; + break; + case QtWarningMsg: + str << "WARN "; + break; + case QtFatalMsg: + str << "FATAL"; + break; + case QtCriticalMsg: + str << "CRITICAL"; + break; + } + str << "] " << context.category << ": "; + if (context.file && *context.file && context.line) { + str << context.file << ":" << context.line << ": "; + } + if (context.function && *context.function) { + str << context.function << ": "; + } + str << message << "\n"; + str.flush(); + file.write(buf.constData(), buf.size()); + file.flush(); + + if (oldHandler && (strcmp(context.category, "quotient.e2ee") != 0 || e2eeDebugEnabled)) { + oldHandler(type, context, message); + } + } + + void setName(const QString &appName) + { + name = appName; + + if (file.isOpen()) { + file.close(); + } + auto filePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QDir::separator() + appName; + + QFileInfo infoOld(filePath + QLatin1String(".old")); + if (infoOld.exists()) { + QFile fileOld(infoOld.absoluteFilePath()); + const bool success = fileOld.remove(); + if (!success) { + qFatal("Cannot remove old log file '%s': %s", qUtf8Printable(fileOld.fileName()), qUtf8Printable(fileOld.errorString())); + } + } + + QFileInfo info(filePath); + if (info.exists()) { + QFile file(info.absoluteFilePath()); + const QString oldName = filePath + QLatin1String(".old"); + const bool success = file.copy(oldName); + if (!success) { + qFatal("Cannot rename log file '%s' to '%s': %s", qUtf8Printable(file.fileName()), qUtf8Printable(oldName), qUtf8Printable(file.errorString())); + } + } + + QFileInfo finfo(filePath); + if (!finfo.absoluteDir().exists()) { + QDir().mkpath(finfo.absolutePath()); + } + file.setFileName(filePath); + file.open(QIODevice::WriteOnly | QIODevice::Unbuffered); + } + + void setOrigHandler(QtMessageHandler origHandler_) + { + origHandler = origHandler_; + } + + QMutex mutex; + QFile file; + QString name; + QtMessageHandler origHandler; + QByteArray loggingCategory; +}; + +Q_GLOBAL_STATIC(DebugPrivate, sInstance) + +void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message) +{ + switch (type) { + case QtDebugMsg: + case QtInfoMsg: + case QtWarningMsg: + case QtCriticalMsg: + sInstance()->log(type, context, message); + break; + case QtFatalMsg: + sInstance()->log(QtInfoMsg, context, message); + abort(); + } +} + +void filter(QLoggingCategory *category) +{ + if (qstrcmp(category->categoryName(), "quotient.e2ee") == 0) { + category->setEnabled(QtDebugMsg, true); + } else if (oldCategoryFilter) { + oldCategoryFilter(category); + } +} + +void initLogging() +{ + e2eeDebugEnabled = QLoggingCategory("quotient.e2ee", QtDebugMsg).isEnabled(QtDebugMsg); + oldCategoryFilter = QLoggingCategory::installFilter(filter); + oldHandler = qInstallMessageHandler(messageHandler); + sInstance->setOrigHandler(oldHandler); + sInstance->setName("neochat.log"); +} + +#include "logger.moc" diff --git a/src/logger.h b/src/logger.h new file mode 100644 index 000000000..7e2567641 --- /dev/null +++ b/src/logger.h @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 Tobias Fella +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +/** + * Initlalize logging to file and enables some additional categories, which will only be logged to the file + */ +void initLogging(); diff --git a/src/main.cpp b/src/main.cpp index c9ca63b97..2ecda4084 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,6 +45,7 @@ #include "controller.h" #include "filetypesingleton.h" #include "linkpreviewer.h" +#include "logger.h" #include "login.h" #include "matriximageprovider.h" #include "models/collapsestateproxymodel.h" @@ -180,6 +181,8 @@ int main(int argc, char *argv[]) KAboutData::setApplicationData(about); QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("org.kde.neochat"))); + initLogging(); + #ifdef NEOCHAT_FLATPAK // Copy over the included FontConfig configuration to the // app's config dir: