diff --git a/man/nheko.1.adoc b/man/nheko.1.adoc index 82053af02..d2eacbd4b 100644 --- a/man/nheko.1.adoc +++ b/man/nheko.1.adoc @@ -31,7 +31,27 @@ Displays help including Qt specific options. Displays version information. *--debug*:: -Enables debug output. +Alias for _--log-level trace_. + +*-l*, *--log-level* __:: +Set the global log level, or a comma-separated list of _=_ +pairs, or both. For example, to set the default log level to _warn_ but +disable logging for the _ui_ component, pass _warn,ui=off_. ++ +levels: _trace_ _debug_ _info_ _warning_ _error_ _critical_ _off_ ++ +components: _crypto_ _db_ _mtx_ _net_ _qml_ _ui_ ++ +Log levels can also be set in the NHEKO_LOG_LEVEL environment variable, using +the same syntax. It will be overridden by this command line option. + +*-L*, *--log-type* __:: +Set the log output type. A comma-separated list is allowed. The default is _file,stderr_. ++ +types: _file_ _stderr_ _none_ ++ +The log type can also be set in the NHEKO_LOG_TYPE environment variable, +which will be overridden by this command line option. *-p* __, *--profile* __:: Creates a unique profile, which allows you to log into several accounts at the diff --git a/src/Logging.cpp b/src/Logging.cpp index 9ae94f088..cd72e395f 100644 --- a/src/Logging.cpp +++ b/src/Logging.cpp @@ -6,8 +6,10 @@ #include "Logging.h" #include "config/nheko.h" +#include "spdlog/cfg/helpers.h" #include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/stdout_color_sinks.h" +#include "spdlog/spdlog.h" #include #include @@ -61,19 +63,20 @@ qmlMessageHandler(QtMsgType type, const QMessageLogContext &context, const QStri } namespace nhlog { -bool enable_debug_log_from_commandline = false; void -init(const std::string &file_path) +init(const QString &level, const QString &path, bool to_stderr) { - auto file_sink = std::make_shared( - file_path, MAX_FILE_SIZE, MAX_LOG_FILES); - - auto console_sink = std::make_shared(); - std::vector sinks; - sinks.push_back(file_sink); - sinks.push_back(console_sink); + if (!path.isEmpty()) { + auto file_sink = std::make_shared( + path.toStdString(), MAX_FILE_SIZE, MAX_LOG_FILES); + sinks.push_back(file_sink); + } + if (to_stderr) { + auto console_sink = std::make_shared(); + sinks.push_back(console_sink); + } mtx::utils::log::log()->sinks() = sinks; net_logger = std::make_shared("net", std::begin(sinks), std::end(sinks)); @@ -82,7 +85,7 @@ init(const std::string &file_path) crypto_logger = std::make_shared("crypto", std::begin(sinks), std::end(sinks)); qml_logger = std::make_shared("qml", std::begin(sinks), std::end(sinks)); - if (nheko::enable_debug_log || enable_debug_log_from_commandline) { + if (nheko::enable_debug_log) { db_logger->set_level(spdlog::level::trace); ui_logger->set_level(spdlog::level::trace); crypto_logger->set_level(spdlog::level::trace); @@ -91,6 +94,17 @@ init(const std::string &file_path) mtx::utils::log::log()->set_level(spdlog::level::trace); } + spdlog::register_logger(net_logger); + spdlog::register_logger(ui_logger); + spdlog::register_logger(db_logger); + spdlog::register_logger(crypto_logger); + spdlog::register_logger(qml_logger); + // We assume the mtxclient library will register its own logger. + + if (!level.isEmpty()) { + spdlog::cfg::helpers::load_levels(level.toStdString()); + } + qInstallMessageHandler(qmlMessageHandler); } diff --git a/src/Logging.h b/src/Logging.h index 4a5109a62..23ff82361 100644 --- a/src/Logging.h +++ b/src/Logging.h @@ -6,11 +6,15 @@ #pragma once #include -#include +#include + +#include + +#include "spdlog/logger.h" namespace nhlog { void -init(const std::string &file); +init(const QString &level, const QString &path, bool to_stderr); std::shared_ptr ui(); @@ -27,5 +31,4 @@ crypto(); std::shared_ptr qml(); -extern bool enable_debug_log_from_commandline; } diff --git a/src/main.cpp b/src/main.cpp index 47ebba27d..f76d8ca63 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -202,8 +202,24 @@ main(int argc, char *argv[]) QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); - QCommandLineOption debugOption(QStringLiteral("debug"), QStringLiteral("Enable debug output")); + QCommandLineOption debugOption(QStringLiteral("debug"), + QObject::tr("Alias for '--log-level trace'.")); parser.addOption(debugOption); + QCommandLineOption logLevel( + QStringList() << QStringLiteral("l") << QStringLiteral("log-level"), + QObject::tr("Set the global log level, or a comma-separated list of = " + "pairs, or both. For example, to set the default log level to 'warn' but " + "disable logging for the 'ui' component, pass 'warn,ui=off'. " + "levels:{trace,debug,info,warning,error,critical,off} " + "components:{crypto,db,mtx,net,qml,ui}"), + QObject::tr("level")); + parser.addOption(logLevel); + QCommandLineOption logType( + QStringList() << QStringLiteral("L") << QStringLiteral("log-type"), + QObject::tr("Set the log output type. A comma-separated list is allowed. " + "The default is 'file,stderr'. types:{file,stderr,none}"), + QObject::tr("type")); + parser.addOption(logType); // This option is not actually parsed via Qt due to the need to parse it before the app // name is set. It only exists to keep Qt from complaining about the --profile/-p @@ -254,15 +270,36 @@ main(int argc, char *argv[]) } #endif - if (parser.isSet(debugOption)) - nhlog::enable_debug_log_from_commandline = true; - try { - nhlog::init(QStringLiteral("%1/nheko.log") - .arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) - .toStdString()); + QString level; + if (parser.isSet(logLevel)) { + level = parser.value(logLevel); + } else if (parser.isSet(debugOption)) { + level = "trace"; + } else { + level = qEnvironmentVariable("NHEKO_LOG_LEVEL"); + } + + QStringList targets = + (parser.isSet(logType) ? parser.value(logType) + : qEnvironmentVariable("NHEKO_LOG_TYPE", "file,stderr")) + .split(',', Qt::SkipEmptyParts); + targets.removeAll("none"); + bool to_stderr = bool(targets.removeAll("stderr")); + QString path = targets.removeAll("file") + ? QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) + .filePath("nheko.log") + : QLatin1String(""); + if (!targets.isEmpty()) { + std::cerr << "Invalid log type '" << targets.first().toStdString().c_str() << "'" + << std::endl; + std::exit(1); + } + + nhlog::init(level, path, to_stderr); + } catch (const spdlog::spdlog_ex &ex) { - std::cout << "Log initialization failed: " << ex.what() << std::endl; + std::cerr << "Log initialization failed: " << ex.what() << std::endl; std::exit(1); }