From 8364e94d266bbde6e1a13f901e60e62fdbfefed2 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Wed, 7 Aug 2024 15:53:11 -0700 Subject: [PATCH] core/log: capture early logs in fs logger --- src/core/CMakeLists.txt | 1 - src/core/filelogger.cpp | 60 ------------------- src/core/filelogger.hpp | 13 ---- src/core/filelogger_p.hpp | 24 -------- src/core/logging.cpp | 123 +++++++++++++++++++++++++++++++++++++- src/core/logging.hpp | 36 +++++++++++ src/core/main.cpp | 7 ++- src/core/paths.cpp | 1 - 8 files changed, 160 insertions(+), 105 deletions(-) delete mode 100644 src/core/filelogger.cpp delete mode 100644 src/core/filelogger.hpp delete mode 100644 src/core/filelogger_p.hpp diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 83013907..5ced5410 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -39,7 +39,6 @@ qt_add_library(quickshell-core STATIC clock.cpp logging.cpp paths.cpp - filelogger.cpp ) set_source_files_properties(main.cpp PROPERTIES COMPILE_DEFINITIONS GIT_REVISION="${GIT_REVISION}") diff --git a/src/core/filelogger.cpp b/src/core/filelogger.cpp deleted file mode 100644 index 7dcada9e..00000000 --- a/src/core/filelogger.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "filelogger.hpp" - -#include -#include -#include -#include -#include -#include - -#include "filelogger_p.hpp" -#include "logging.hpp" -#include "paths.hpp" - -Q_LOGGING_CATEGORY(logLogger, "quickshell.logger", QtWarningMsg); - -void FileLoggerThread::init() { - auto* thread = new FileLoggerThread(); - auto* logger = new FileLogger(); - logger->moveToThread(thread); - thread->start(); - QMetaObject::invokeMethod(logger, "init", Qt::BlockingQueuedConnection); -} - -void FileLogger::init() { - qCDebug(logLogger) << "Initializing filesystem logger..."; - auto* runDir = QsPaths::instance()->instanceRunDir(); - - if (!runDir) { - qCCritical(logLogger - ) << "Could not start filesystem logger as the runtime directory could not be created."; - return; - } - - auto path = runDir->filePath("log.log"); - auto* file = new QFile(path); - - if (!file->open(QFile::WriteOnly | QFile::Truncate)) { - qCCritical(logLogger - ) << "Could not start filesystem logger as the log file could not be created:" - << path; - return; - } - - this->fileStream.setDevice(file); - - QObject::connect( - LogManager::instance(), - &LogManager::logMessage, - this, - &FileLogger::onMessage, - Qt::QueuedConnection - ); - - qDebug(logLogger) << "Initialized filesystem logger"; -} - -void FileLogger::onMessage(const LogMessage& msg) { - LogManager::formatMessage(this->fileStream, msg, false, true); - this->fileStream << Qt::endl; -} diff --git a/src/core/filelogger.hpp b/src/core/filelogger.hpp deleted file mode 100644 index dba7aaa2..00000000 --- a/src/core/filelogger.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include -#include -class FileLoggerThread: public QThread { - Q_OBJECT; - -public: - static void init(); - -private: - explicit FileLoggerThread() = default; -}; diff --git a/src/core/filelogger_p.hpp b/src/core/filelogger_p.hpp deleted file mode 100644 index e6d7b633..00000000 --- a/src/core/filelogger_p.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "logging.hpp" - -class FileLogger: public QObject { - Q_OBJECT; - -public: - explicit FileLogger() = default; - -public slots: - void init(); - -private slots: - void onMessage(const LogMessage& msg); - -private: - QTextStream fileStream; -}; diff --git a/src/core/logging.cpp b/src/core/logging.cpp index 8b567e70..642d3fc6 100644 --- a/src/core/logging.cpp +++ b/src/core/logging.cpp @@ -3,14 +3,25 @@ #include #include +#include +#include +#include +#include #include #include #include +#include #include +#include +#include -LogManager::LogManager(): colorLogs(qEnvironmentVariableIsEmpty("NO_COLOR")), stdoutStream(stdout) { - qInstallMessageHandler(&LogManager::messageHandler); -} +#include "paths.hpp" + +Q_LOGGING_CATEGORY(logLogging, "quickshell.logging", QtWarningMsg); + +LogManager::LogManager() + : colorLogs(qEnvironmentVariableIsEmpty("NO_COLOR")) + , stdoutStream(stdout) {} void LogManager::messageHandler( QtMsgType type, @@ -32,6 +43,27 @@ LogManager* LogManager::instance() { return instance; } +void LogManager::init() { + auto* instance = LogManager::instance(); + + qInstallMessageHandler(&LogManager::messageHandler); + + qCDebug(logLogging) << "Creating offthread logger..."; + auto* thread = new QThread(); + instance->threadProxy.moveToThread(thread); + thread->start(); + QMetaObject::invokeMethod(&instance->threadProxy, "initInThread", Qt::BlockingQueuedConnection); + qCDebug(logLogging) << "Logger initialized."; +} + +void LogManager::initFs() { + QMetaObject::invokeMethod( + &LogManager::instance()->threadProxy, + "initFs", + Qt::BlockingQueuedConnection + ); +} + void LogManager::formatMessage( QTextStream& stream, const LogMessage& msg, @@ -75,3 +107,88 @@ void LogManager::formatMessage( if (color && msg.type == QtFatalMsg) stream << "\033[0m"; } + +void LoggingThreadProxy::initInThread() { + this->logging = new ThreadLogging(this); + this->logging->init(); +} + +void LoggingThreadProxy::initFs() { this->logging->initFs(); } + +void ThreadLogging::init() { + auto mfd = memfd_create("quickshell:logs", 0); + + if (mfd == -1) { + qCCritical(logLogging) << "Failed to create memfd for initial log storage" + << qt_error_string(-1); + return; + } + + this->file = new QFile(); + this->file->open(mfd, QFile::WriteOnly, QFile::AutoCloseHandle); + this->fileStream.setDevice(this->file); + + // This connection is direct so it works while the event loop is destroyed between + // QCoreApplication delete and Q(Gui)Application launch. + QObject::connect( + LogManager::instance(), + &LogManager::logMessage, + this, + &ThreadLogging::onMessage, + Qt::DirectConnection + ); + + qCDebug(logLogging) << "Created memfd" << mfd << "for early logs."; +} + +void ThreadLogging::initFs() { + + qCDebug(logLogging) << "Starting filesystem logging..."; + auto* runDir = QsPaths::instance()->instanceRunDir(); + + if (!runDir) { + qCCritical(logLogging + ) << "Could not start filesystem logging as the runtime directory could not be created."; + return; + } + + auto path = runDir->filePath("log.log"); + auto* file = new QFile(path); + + if (!file->open(QFile::WriteOnly | QFile::Truncate)) { + qCCritical(logLogging + ) << "Could not start filesystem logger as the log file could not be created:" + << path; + return; + } + + qCDebug(logLogging) << "Copying memfd logs to log file..."; + + auto* oldFile = this->file; + oldFile->seek(0); + sendfile(file->handle(), oldFile->handle(), nullptr, oldFile->size()); + this->file = file; + this->fileStream.setDevice(file); + delete oldFile; + + qCDebug(logLogging) << "Switched logging to disk logs."; + + auto* logManager = LogManager::instance(); + QObject::disconnect(logManager, &LogManager::logMessage, this, &ThreadLogging::onMessage); + + QObject::connect( + logManager, + &LogManager::logMessage, + this, + &ThreadLogging::onMessage, + Qt::QueuedConnection + ); + + qCDebug(logLogging) << "Switched threaded logger to queued eventloop connection."; +} + +void ThreadLogging::onMessage(const LogMessage& msg) { + if (this->fileStream.device() == nullptr) return; + LogManager::formatMessage(this->fileStream, msg, false, true); + this->fileStream << Qt::endl; +} diff --git a/src/core/logging.hpp b/src/core/logging.hpp index ae9e596e..f831fcfa 100644 --- a/src/core/logging.hpp +++ b/src/core/logging.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -26,10 +27,44 @@ struct LogMessage { QByteArray body; }; +class ThreadLogging: public QObject { + Q_OBJECT; + +public: + explicit ThreadLogging(QObject* parent): QObject(parent) {} + + void init(); + void initFs(); + void setupFileLogging(); + +private slots: + void onMessage(const LogMessage& msg); + +private: + QFile* file = nullptr; + QTextStream fileStream; +}; + +class LoggingThreadProxy: public QObject { + Q_OBJECT; + +public: + explicit LoggingThreadProxy() = default; + +public slots: + void initInThread(); + void initFs(); + +private: + ThreadLogging* logging = nullptr; +}; + class LogManager: public QObject { Q_OBJECT; public: + static void init(); + static void initFs(); static LogManager* instance(); static void formatMessage(QTextStream& stream, const LogMessage& msg, bool color, bool timestamp); @@ -43,4 +78,5 @@ private: bool colorLogs; QTextStream stdoutStream; + LoggingThreadProxy threadProxy; }; diff --git a/src/core/main.cpp b/src/core/main.cpp index e2bbdcbe..cc71f9fb 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -22,14 +22,12 @@ #include #include -#include "filelogger.hpp" #include "logging.hpp" #include "paths.hpp" #include "plugin.hpp" #include "rootwrapper.hpp" int qs_main(int argc, char** argv) { - LogManager::instance(); QString configFilePath; QString workingDirectory; @@ -48,6 +46,9 @@ int qs_main(int argc, char** argv) { QCoreApplication::setApplicationName("quickshell"); QCoreApplication::setApplicationVersion("0.1.0 (" GIT_REVISION ")"); + // Start log manager - has to happen with an active event loop or offthread can't be started. + LogManager::init(); + QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); @@ -390,7 +391,7 @@ int qs_main(int argc, char** argv) { app = new QGuiApplication(argc, argv); } - FileLoggerThread::init(); + LogManager::initFs(); if (debugPort != -1) { QQmlDebuggingEnabler::enableDebugging(true); diff --git a/src/core/paths.cpp b/src/core/paths.cpp index b204f94f..7e05530d 100644 --- a/src/core/paths.cpp +++ b/src/core/paths.cpp @@ -21,7 +21,6 @@ void QsPaths::init(QString shellId) { QsPaths::instance()->shellId = std::move(s QDir* QsPaths::cacheDir() { if (this->cacheState == DirState::Unknown) { auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); - dir = QDir(dir.filePath("quickshell")); dir = QDir(dir.filePath(this->shellId)); this->mCacheDir = dir;