forked from quickshell/quickshell
		
	core/log: add filesystem logger
This commit is contained in:
		
							parent
							
								
									46f48f2f87
								
							
						
					
					
						commit
						6bf4826ae7
					
				
					 9 changed files with 299 additions and 53 deletions
				
			
		| 
						 | 
				
			
			@ -38,6 +38,8 @@ qt_add_library(quickshell-core STATIC
 | 
			
		|||
	qsmenuanchor.cpp
 | 
			
		||||
	clock.cpp
 | 
			
		||||
	logging.cpp
 | 
			
		||||
	paths.cpp
 | 
			
		||||
	filelogger.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set_source_files_properties(main.cpp PROPERTIES COMPILE_DEFINITIONS GIT_REVISION="${GIT_REVISION}")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										60
									
								
								src/core/filelogger.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/core/filelogger.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,60 @@
 | 
			
		|||
#include "filelogger.hpp"
 | 
			
		||||
 | 
			
		||||
#include <qlogging.h>
 | 
			
		||||
#include <qloggingcategory.h>
 | 
			
		||||
#include <qnamespace.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qobjectdefs.h>
 | 
			
		||||
#include <qtextstream.h>
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
	this->fileStream << Qt::endl;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								src/core/filelogger.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/core/filelogger.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <qthread.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
class FileLoggerThread: public QThread {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static void init();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	explicit FileLoggerThread() = default;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										24
									
								
								src/core/filelogger_p.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/core/filelogger_p.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <qfile.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qtextstream.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
 | 
			
		||||
#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;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -5,70 +5,61 @@
 | 
			
		|||
#include <qlogging.h>
 | 
			
		||||
#include <qstring.h>
 | 
			
		||||
#include <qtenvironmentvariables.h>
 | 
			
		||||
#include <qtextstream.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
LogManager::LogManager(): colorLogs(qEnvironmentVariableIsEmpty("NO_COLOR")), stdoutStream(stdout) {
 | 
			
		||||
	qInstallMessageHandler(&LogManager::messageHandler);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool COLOR_LOGS = false; // NOLINT
 | 
			
		||||
 | 
			
		||||
void formatMessage(
 | 
			
		||||
void LogManager::messageHandler(
 | 
			
		||||
    QtMsgType type,
 | 
			
		||||
    const QMessageLogContext& context,
 | 
			
		||||
    const QString& msg,
 | 
			
		||||
    bool color
 | 
			
		||||
    const QString& msg
 | 
			
		||||
) {
 | 
			
		||||
	const auto* typeString = "[log error]";
 | 
			
		||||
	auto message = LogMessage(type, context.category, msg.toUtf8());
 | 
			
		||||
 | 
			
		||||
	auto* self = LogManager::instance();
 | 
			
		||||
 | 
			
		||||
	LogManager::formatMessage(self->stdoutStream, message, self->colorLogs);
 | 
			
		||||
	self->stdoutStream << Qt::endl;
 | 
			
		||||
 | 
			
		||||
	emit self->logMessage(message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LogManager* LogManager::instance() {
 | 
			
		||||
	static auto* instance = new LogManager(); // NOLINT
 | 
			
		||||
	return instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LogManager::formatMessage(QTextStream& stream, const LogMessage& msg, bool color) {
 | 
			
		||||
	if (color) {
 | 
			
		||||
		switch (type) {
 | 
			
		||||
		case QtDebugMsg: typeString = "\033[34m DEBUG"; break;
 | 
			
		||||
		case QtInfoMsg: typeString = "\033[32m  INFO"; break;
 | 
			
		||||
		case QtWarningMsg: typeString = "\033[33m  WARN"; break;
 | 
			
		||||
		case QtCriticalMsg: typeString = "\033[31m ERROR"; break;
 | 
			
		||||
		case QtFatalMsg: typeString = "\033[31m FATAL"; break;
 | 
			
		||||
		switch (msg.type) {
 | 
			
		||||
		case QtDebugMsg: stream << "\033[34m DEBUG"; break;
 | 
			
		||||
		case QtInfoMsg: stream << "\033[32m  INFO"; break;
 | 
			
		||||
		case QtWarningMsg: stream << "\033[33m  WARN"; break;
 | 
			
		||||
		case QtCriticalMsg: stream << "\033[31m ERROR"; break;
 | 
			
		||||
		case QtFatalMsg: stream << "\033[31m FATAL"; break;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		switch (type) {
 | 
			
		||||
		case QtDebugMsg: typeString = " DEBUG"; break;
 | 
			
		||||
		case QtInfoMsg: typeString = "  INFO"; break;
 | 
			
		||||
		case QtWarningMsg: typeString = "  WARN"; break;
 | 
			
		||||
		case QtCriticalMsg: typeString = " ERROR"; break;
 | 
			
		||||
		case QtFatalMsg: typeString = " FATAL"; break;
 | 
			
		||||
		switch (msg.type) {
 | 
			
		||||
		case QtDebugMsg: stream << " DEBUG"; break;
 | 
			
		||||
		case QtInfoMsg: stream << "  INFO"; break;
 | 
			
		||||
		case QtWarningMsg: stream << "  WARN"; break;
 | 
			
		||||
		case QtCriticalMsg: stream << " ERROR"; break;
 | 
			
		||||
		case QtFatalMsg: stream << " FATAL"; break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const auto isDefault = strcmp(context.category, "default") == 0;
 | 
			
		||||
	const auto isDefault = strcmp(msg.category, "default") == 0;
 | 
			
		||||
 | 
			
		||||
	const char* format = nullptr;
 | 
			
		||||
	if (color && !isDefault && msg.type != QtFatalMsg) stream << "\033[97m";
 | 
			
		||||
 | 
			
		||||
	if (color) {
 | 
			
		||||
		if (type == QtFatalMsg) {
 | 
			
		||||
			if (isDefault) format = "%s: %s\033[0m\n";
 | 
			
		||||
			else format = "%s %s: %s\033[0m\n";
 | 
			
		||||
		} else {
 | 
			
		||||
			if (isDefault) format = "%s\033[0m: %s\n";
 | 
			
		||||
			else format = "%s \033[97m%s\033[0m: %s\n";
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if (isDefault) format = "%s: %s\n";
 | 
			
		||||
		else format = "%s %s: %s\n";
 | 
			
		||||
	if (!isDefault) {
 | 
			
		||||
		stream << ' ' << msg.category;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (isDefault) {
 | 
			
		||||
		printf(format, typeString, msg.toStdString().c_str());
 | 
			
		||||
	} else {
 | 
			
		||||
		printf(format, typeString, context.category, msg.toStdString().c_str());
 | 
			
		||||
	}
 | 
			
		||||
	if (color && msg.type != QtFatalMsg) stream << "\033[0m";
 | 
			
		||||
 | 
			
		||||
	fflush(stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) {
 | 
			
		||||
	formatMessage(type, context, msg, COLOR_LOGS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
void LogManager::setup() {
 | 
			
		||||
	COLOR_LOGS = qEnvironmentVariableIsEmpty("NO_COLOR");
 | 
			
		||||
	qInstallMessageHandler(&messageHandler);
 | 
			
		||||
	stream << ": " << msg.body;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,38 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
class LogManager {
 | 
			
		||||
public:
 | 
			
		||||
	static void setup();
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include <qlogging.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qtextstream.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
 | 
			
		||||
struct LogMessage {
 | 
			
		||||
	explicit LogMessage(QtMsgType type, const char* category, QByteArray body)
 | 
			
		||||
	    : type(type)
 | 
			
		||||
	    , category(category)
 | 
			
		||||
	    , body(std::move(body)) {}
 | 
			
		||||
 | 
			
		||||
	QtMsgType type;
 | 
			
		||||
	const char* category;
 | 
			
		||||
	QByteArray body;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class LogManager: public QObject {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	static LogManager* instance();
 | 
			
		||||
 | 
			
		||||
	static void formatMessage(QTextStream& stream, const LogMessage& msg, bool color);
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	void logMessage(LogMessage msg);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	explicit LogManager();
 | 
			
		||||
	static void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
 | 
			
		||||
 | 
			
		||||
	bool colorLogs;
 | 
			
		||||
	QTextStream stdoutStream;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,12 +22,14 @@
 | 
			
		|||
#include <qtextstream.h>
 | 
			
		||||
#include <qtpreprocessorsupport.h>
 | 
			
		||||
 | 
			
		||||
#include "filelogger.hpp"
 | 
			
		||||
#include "logging.hpp"
 | 
			
		||||
#include "paths.hpp"
 | 
			
		||||
#include "plugin.hpp"
 | 
			
		||||
#include "rootwrapper.hpp"
 | 
			
		||||
 | 
			
		||||
int qs_main(int argc, char** argv) {
 | 
			
		||||
	LogManager::setup();
 | 
			
		||||
	LogManager::instance();
 | 
			
		||||
	QString configFilePath;
 | 
			
		||||
	QString workingDirectory;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -340,6 +342,8 @@ int qs_main(int argc, char** argv) {
 | 
			
		|||
		qputenv(var.toUtf8(), val.toUtf8());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	QsPaths::init(shellId);
 | 
			
		||||
 | 
			
		||||
	// While the simple animation driver can lead to better animations in some cases,
 | 
			
		||||
	// it also can cause excessive repainting at excessively high framerates which can
 | 
			
		||||
	// lead to noticeable amounts of gpu usage, including overheating on some systems.
 | 
			
		||||
| 
						 | 
				
			
			@ -386,6 +390,8 @@ int qs_main(int argc, char** argv) {
 | 
			
		|||
		app = new QGuiApplication(argc, argv);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	FileLoggerThread::init();
 | 
			
		||||
 | 
			
		||||
	if (debugPort != -1) {
 | 
			
		||||
		QQmlDebuggingEnabler::enableDebugging(true);
 | 
			
		||||
		auto wait = waitForDebug ? QQmlDebuggingEnabler::WaitForClient
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										91
									
								
								src/core/paths.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/core/paths.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,91 @@
 | 
			
		|||
#include "paths.hpp"
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include <qdatetime.h>
 | 
			
		||||
#include <qdir.h>
 | 
			
		||||
#include <qlogging.h>
 | 
			
		||||
#include <qloggingcategory.h>
 | 
			
		||||
#include <qstandardpaths.h>
 | 
			
		||||
#include <qtenvironmentvariables.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
Q_LOGGING_CATEGORY(logPaths, "quickshell.paths", QtWarningMsg);
 | 
			
		||||
 | 
			
		||||
QsPaths* QsPaths::instance() {
 | 
			
		||||
	static auto* instance = new QsPaths(); // NOLINT
 | 
			
		||||
	return instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QsPaths::init(QString shellId) { QsPaths::instance()->shellId = std::move(shellId); }
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
		qCDebug(logPaths) << "Initialized cache path:" << dir.path();
 | 
			
		||||
 | 
			
		||||
		if (!dir.mkpath(".")) {
 | 
			
		||||
			qCCritical(logPaths) << "Cannot create cache directory at" << dir.path();
 | 
			
		||||
 | 
			
		||||
			this->cacheState = DirState::Failed;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (this->cacheState == DirState::Failed) return nullptr;
 | 
			
		||||
	else return &this->mCacheDir;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QDir* QsPaths::runDir() {
 | 
			
		||||
	if (this->runState == DirState::Unknown) {
 | 
			
		||||
		auto runtimeDir = qEnvironmentVariable("XDG_RUNTIME_DIR");
 | 
			
		||||
		if (runtimeDir.isEmpty()) {
 | 
			
		||||
			runtimeDir = QString("/run/user/$1").arg(getuid());
 | 
			
		||||
			qCInfo(logPaths) << "XDG_RUNTIME_DIR was not set, defaulting to" << runtimeDir;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto dir = QDir(runtimeDir);
 | 
			
		||||
		dir = QDir(dir.filePath("quickshell"));
 | 
			
		||||
		dir = QDir(dir.filePath(this->shellId));
 | 
			
		||||
		this->mRunDir = dir;
 | 
			
		||||
 | 
			
		||||
		qCDebug(logPaths) << "Initialized runtime path:" << dir.path();
 | 
			
		||||
 | 
			
		||||
		if (!dir.mkpath(".")) {
 | 
			
		||||
			qCCritical(logPaths) << "Cannot create runtime directory at" << dir.path();
 | 
			
		||||
 | 
			
		||||
			this->runState = DirState::Failed;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (this->runState == DirState::Failed) return nullptr;
 | 
			
		||||
	else return &this->mRunDir;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QDir* QsPaths::instanceRunDir() {
 | 
			
		||||
	if (this->instanceRunState == DirState::Unknown) {
 | 
			
		||||
		auto* runtimeDir = this->runDir();
 | 
			
		||||
 | 
			
		||||
		if (!runtimeDir) {
 | 
			
		||||
			qCCritical(logPaths) << "Cannot create instance runtime directory as main runtim directory "
 | 
			
		||||
			                        "could not be created.";
 | 
			
		||||
			this->instanceRunState = DirState::Failed;
 | 
			
		||||
		} else {
 | 
			
		||||
			this->mInstanceRunDir =
 | 
			
		||||
			    runtimeDir->filePath(QString("run-%1").arg(QDateTime::currentMSecsSinceEpoch()));
 | 
			
		||||
 | 
			
		||||
			qCDebug(logPaths) << "Initialized instance runtime path:" << this->mInstanceRunDir.path();
 | 
			
		||||
 | 
			
		||||
			if (!this->mInstanceRunDir.mkpath(".")) {
 | 
			
		||||
				qCCritical(logPaths) << "Cannot create instance runtime directory at"
 | 
			
		||||
				                     << this->mInstanceRunDir.path();
 | 
			
		||||
				this->instanceRunState = DirState::Failed;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (this->runState == DirState::Failed) return nullptr;
 | 
			
		||||
	else return &this->mInstanceRunDir;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								src/core/paths.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/core/paths.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include <qdir.h>
 | 
			
		||||
 | 
			
		||||
class QsPaths {
 | 
			
		||||
public:
 | 
			
		||||
	static QsPaths* instance();
 | 
			
		||||
	static void init(QString shellId);
 | 
			
		||||
 | 
			
		||||
	QDir* cacheDir();
 | 
			
		||||
	QDir* runDir();
 | 
			
		||||
	QDir* instanceRunDir();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	enum class DirState {
 | 
			
		||||
		Unknown = 0,
 | 
			
		||||
		Ready = 1,
 | 
			
		||||
		Failed = 2,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	QString shellId;
 | 
			
		||||
	QDir mCacheDir;
 | 
			
		||||
	QDir mRunDir;
 | 
			
		||||
	QDir mInstanceRunDir;
 | 
			
		||||
	DirState cacheState = DirState::Unknown;
 | 
			
		||||
	DirState runState = DirState::Unknown;
 | 
			
		||||
	DirState instanceRunState = DirState::Unknown;
 | 
			
		||||
};
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue