diff --git a/loader/resources/mod.json.in b/loader/resources/mod.json.in index ddef6aaf2..76cb44f3a 100644 --- a/loader/resources/mod.json.in +++ b/loader/resources/mod.json.in @@ -110,6 +110,21 @@ ], "requires-restart": true }, + "console-log-level": { + "type": "string", + "default": "info", + "name": "Console Log Level", + "description": "Sets the log level for the platform console.", + "platforms": ["win", "mac"], + "one-of": ["debug", "info", "warn", "error"] + }, + "file-log-level": { + "type": "string", + "default": "info", + "name": "File Log Level", + "description": "Sets the log level for the log files.", + "one-of": ["debug", "info", "warn", "error"] + }, "server-cache-size-limit": { "type": "int", "default": 20, diff --git a/loader/src/load.cpp b/loader/src/load.cpp index df0c6d433..2bb68f52e 100644 --- a/loader/src/load.cpp +++ b/loader/src/load.cpp @@ -108,7 +108,6 @@ bool safeModeCheck() { int geodeEntry(void* platformData) { thread::setName("Main"); - log::Logger::get()->setup(); console::setup(); if (LoaderImpl::get()->isForwardCompatMode()) { console::openIfClosed(); @@ -152,6 +151,10 @@ int geodeEntry(void* platformData) { } } + // Setup logger here so that internal mod is setup and we can read log level + // Logging before this point does store the log, and everything gets logged in this setup call + log::Logger::get()->setup(); + tryShowForwardCompat(); // open console diff --git a/loader/src/loader/LoaderImpl.cpp b/loader/src/loader/LoaderImpl.cpp index a6f6a02c7..209d32b17 100644 --- a/loader/src/loader/LoaderImpl.cpp +++ b/loader/src/loader/LoaderImpl.cpp @@ -80,28 +80,28 @@ Result<> Loader::Impl::setup() { } if (this->supportsLaunchArguments()) { - log::debug("Loading launch arguments"); + log::info("Loading launch arguments"); log::NestScope nest; this->initLaunchArguments(); } // on some platforms, using the crash handler overrides more convenient native handlers if (!this->getLaunchFlag("disable-crash-handler")) { - log::debug("Setting up crash handler"); + log::info("Setting up crash handler"); log::NestScope nest; if (!crashlog::setupPlatformHandler()) { log::debug("Failed to set up crash handler"); } } else { - log::debug("Crash handler setup skipped"); + log::info("Crash handler setup skipped"); } - log::debug("Loading hooks"); + log::info("Loading hooks"); if (log::NestScope nest; !this->loadHooks()) { return Err("There were errors loading some hooks, see console for details"); } - log::debug("Setting up directories"); + log::info("Setting up directories"); { log::NestScope nest; this->createDirectories(); @@ -112,6 +112,7 @@ Result<> Loader::Impl::setup() { // this function is already on the gd thread, so this should be fine ModStateEvent(Mod::get(), ModEventType::Loaded).post(); + log::info("Refreshing mod graph"); this->refreshModGraph(); m_isSetup = true; @@ -405,18 +406,18 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) { } if (node->hasUnresolvedDependencies()) { - log::debug("{} {} has unresolved dependencies", node->getID(), node->getVersion()); + log::warn("{} {} has unresolved dependencies", node->getID(), node->getVersion()); return; } if (node->hasUnresolvedIncompatibilities()) { - log::debug("{} {} has unresolved incompatibilities", node->getID(), node->getVersion()); + log::warn("{} {} has unresolved incompatibilities", node->getID(), node->getVersion()); return; } log::NestScope nest; if (node->isEnabled()) { - log::warn("Mod {} already loaded, this should never happen", node->getID()); + log::error("Mod {} already loaded, this should never happen", node->getID()); return; } @@ -426,16 +427,14 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) { m_lateRefreshedModCount += early ? 0 : 1; auto unzipFunction = [this, node]() { - log::debug("Unzip"); - log::NestScope nest; + log::debug("Unzipping .geode file"); auto res = node->m_impl->unzipGeodeFile(node->getMetadata()); return res; }; auto loadFunction = [this, node, early]() { if (node->shouldLoad()) { - log::debug("Load"); - log::NestScope nest; + log::debug("Loading binary"); auto res = node->m_impl->loadBinary(); if (!res) { this->addProblem({ @@ -513,7 +512,7 @@ void Loader::Impl::findProblems() { continue; } if (mod->targetsOutdatedVersion()) { - log::debug("{} is outdated", id); + log::warn("{} is outdated", id); continue; } log::debug("{}", id); @@ -536,7 +535,7 @@ void Loader::Impl::findProblems() { log::info("{} suggests {} {}", id, dep.id, dep.version); } else { - log::info("{} suggests {} {}, but that suggestion was dismissed", id, dep.id, dep.version); + log::debug("{} suggests {} {}, but that suggestion was dismissed", id, dep.id, dep.version); } break; case ModMetadata::Dependency::Importance::Recommended: @@ -546,10 +545,10 @@ void Loader::Impl::findProblems() { mod, fmt::format("{} {}", dep.id, dep.version.toString()) }); - log::warn("{} recommends {} {}", id, dep.id, dep.version); + log::info("{} recommends {} {}", id, dep.id, dep.version); } else { - log::warn("{} recommends {} {}, but that suggestion was dismissed", id, dep.id, dep.version); + log::debug("{} recommends {} {}, but that suggestion was dismissed", id, dep.id, dep.version); } break; case ModMetadata::Dependency::Importance::Required: @@ -648,7 +647,6 @@ void Loader::Impl::findProblems() { } void Loader::Impl::refreshModGraph() { - log::info("Refreshing mod graph"); log::NestScope nest; if (m_isSetup) { @@ -661,7 +659,7 @@ void Loader::Impl::refreshModGraph() { m_problems.clear(); m_loadingState = LoadingState::Queue; - log::debug("Queueing mods"); + log::info("Queueing mods"); std::vector modQueue; { log::NestScope nest; @@ -669,7 +667,7 @@ void Loader::Impl::refreshModGraph() { } m_loadingState = LoadingState::List; - log::debug("Populating mod list"); + log::info("Populating mod list"); { log::NestScope nest; this->populateModList(modQueue); @@ -677,36 +675,38 @@ void Loader::Impl::refreshModGraph() { } m_loadingState = LoadingState::Graph; - log::debug("Building mod graph"); + log::info("Building mod graph"); { log::NestScope nest; this->buildModGraph(); } - log::debug("Ordering mod stack"); + log::info("Ordering mod stack"); { log::NestScope nest; this->orderModStack(); } m_loadingState = LoadingState::EarlyMods; - log::debug("Loading early mods"); + log::info("Loading early mods"); { log::NestScope nest; while (!m_modsToLoad.empty() && m_modsToLoad.front()->needsEarlyLoad()) { auto mod = m_modsToLoad.front(); m_modsToLoad.pop_front(); + log::info("Loading mod {} {}", mod->getID(), mod->getVersion()); this->loadModGraph(mod, true); } } auto end = std::chrono::high_resolution_clock::now(); auto time = std::chrono::duration_cast(end - begin).count(); - log::info("Took {}s. Continuing next frame...", static_cast(time) / 1000.f); + log::debug("Took {}s. Continuing next frame...", static_cast(time) / 1000.f); m_loadingState = LoadingState::Mods; queueInMainThread([this]() { + log::info("Loading non-early mods"); this->continueRefreshModGraph(); }); } @@ -769,10 +769,10 @@ void Loader::Impl::continueRefreshModGraph() { if (m_lateRefreshedModCount > 0) { auto end = std::chrono::high_resolution_clock::now(); auto time = std::chrono::duration_cast(end - m_timerBegin).count(); - log::info("Took {}s", static_cast(time) / 1000.f); + log::debug("Took {}s", static_cast(time) / 1000.f); } - log::info("Continuing mod graph refresh..."); + log::debug("Continuing mod graph refresh..."); log::NestScope nest; m_timerBegin = std::chrono::high_resolution_clock::now(); @@ -782,14 +782,14 @@ void Loader::Impl::continueRefreshModGraph() { if (!m_modsToLoad.empty()) { auto mod = m_modsToLoad.front(); m_modsToLoad.pop_front(); - log::debug("Loading mod {} {}", mod->getID(), mod->getVersion()); + log::info("Loading mod {} {}", mod->getID(), mod->getVersion()); this->loadModGraph(mod, false); break; } m_loadingState = LoadingState::Problems; [[fallthrough]]; case LoadingState::Problems: - log::debug("Finding problems"); + log::info("Finding problems"); { log::NestScope nest; this->findProblems(); @@ -798,7 +798,7 @@ void Loader::Impl::continueRefreshModGraph() { { auto end = std::chrono::high_resolution_clock::now(); auto time = std::chrono::duration_cast(end - m_timerBegin).count(); - log::info("Took {}s", static_cast(time) / 1000.f); + log::debug("Took {}s", static_cast(time) / 1000.f); } break; default: diff --git a/loader/src/loader/Log.cpp b/loader/src/loader/Log.cpp index 84d606f3d..4ead90dd9 100644 --- a/loader/src/loader/Log.cpp +++ b/loader/src/loader/Log.cpp @@ -2,8 +2,10 @@ #include "LogImpl.hpp" #include +#include #include #include +#include #include #include #include @@ -216,6 +218,10 @@ Logger* Logger::get() { } void Logger::setup() { + if (m_initialized) { + return; + } + auto logDir = dirs::getGeodeLogDir(); // on the first launch, this doesn't exist yet.. @@ -226,6 +232,19 @@ void Logger::setup() { m_logPath = logDir / log::generateLogName(); m_logStream = std::ofstream(m_logPath); + + // Logs can and will probably be added before setup() is called, so we'll write them now + for (Log const& log : m_logs) { + const std::string logStr = log.toString(); + if (log.getSeverity() >= this->getConsoleLogLevel()) { + console::log(logStr, log.getSeverity()); + } + if (log.getSeverity() >= this->getFileLogLevel()) { + m_logStream << logStr << std::endl; + } + } + + m_initialized = true; } void Logger::deleteOldLogs(size_t maxAgeHours) { @@ -260,6 +279,37 @@ std::mutex& getLogMutex() { return mutex; } +Severity Logger::getConsoleLogLevel() { + const std::string level = Mod::get()->getSettingValue("console-log-level"); + if (level == "debug") { + return Severity::Debug; + } else if (level == "info") { + return Severity::Info; + } else if (level == "warn") { + return Severity::Warning; + } else if (level == "error") { + return Severity::Error; + } else { + return Severity::Info; + } +} + +Severity Logger::getFileLogLevel() { + const std::string level = Mod::get()->getSettingValue("file-log-level"); + if (level == "debug") { + return Severity::Debug; + } else if (level == "info") { + return Severity::Info; + } else if (level == "warn") { + return Severity::Warning; + } else if (level == "error") { + return Severity::Error; + } else { + return Severity::Info; + } +} + + void Logger::push(Severity sev, std::string&& thread, std::string&& source, int32_t nestCount, std::string&& content) { std::lock_guard g(getLogMutex()); @@ -267,9 +317,19 @@ void Logger::push(Severity sev, std::string&& thread, std::string&& source, int3 Log& log = m_logs.emplace_back(sev, std::move(thread), std::move(source), nestCount, std::move(content)); + // If logger is not initialized, store the log anyway. When the logger is initialized the pending logs will be logged. + if (!m_initialized) { + return; + } + auto const logStr = log.toString(); - console::log(logStr, log.getSeverity()); - m_logStream << logStr << std::endl; + + if (sev >= this->getConsoleLogLevel()) { + console::log(logStr, log.getSeverity()); + } + if (sev >= this->getFileLogLevel()) { + m_logStream << logStr << std::endl; + } } Nest::Nest(std::shared_ptr impl) : m_impl(std::move(impl)) { } diff --git a/loader/src/loader/LogImpl.hpp b/loader/src/loader/LogImpl.hpp index 2bca6a772..e443c9708 100644 --- a/loader/src/loader/LogImpl.hpp +++ b/loader/src/loader/LogImpl.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ namespace geode::log { class Logger { private: + bool m_initialized = false; std::vector m_logs; std::ofstream m_logStream; std::filesystem::path m_logPath; @@ -43,6 +45,8 @@ namespace geode::log { std::string&& content); std::vector const& list(); + Severity getConsoleLogLevel(); + Severity getFileLogLevel(); void clear(); std::filesystem::path const& getLogPath() const; diff --git a/loader/src/loader/updater.cpp b/loader/src/loader/updater.cpp index fa31dee10..db5917e07 100644 --- a/loader/src/loader/updater.cpp +++ b/loader/src/loader/updater.cpp @@ -221,11 +221,11 @@ void updater::downloadLoaderResources(bool useLatestRelease) { } } if (useLatestRelease) { - log::debug("Loader version {} does not exist, trying to download latest resources", Loader::get()->getVersion().toVString()); + log::info("Loader version {} does not exist, trying to download latest resources", Loader::get()->getVersion().toVString()); downloadLatestLoaderResources(); } else { - log::debug("Loader version {} does not exist on GitHub, not downloading the resources", Loader::get()->getVersion().toVString()); + log::warn("Loader version {} does not exist on GitHub, not downloading the resources", Loader::get()->getVersion().toVString()); ResourceDownloadEvent(UpdateFinished()).post(); } return *response; @@ -361,7 +361,7 @@ void updater::checkForLoaderUpdates() { VersionInfo ver { 0, 0, 0 }; root.needs("tag_name").into(ver); - log::info("Latest version is {}", ver.toVString()); + log::info("Latest Geode version is {}", ver.toVString()); Mod::get()->setSavedValue("latest-version-auto-update-check", ver.toVString()); // make sure release is newer diff --git a/loader/src/platform/mac/LoaderImpl.mm b/loader/src/platform/mac/LoaderImpl.mm index af83a1d89..fbeddea65 100644 --- a/loader/src/platform/mac/LoaderImpl.mm +++ b/loader/src/platform/mac/LoaderImpl.mm @@ -88,8 +88,12 @@ s_isOpen = true; + Severity consoleLevel = log::Logger::get()->getConsoleLogLevel(); + for (auto const& log : log::Logger::get()->list()) { - console::log(log.toString(), log.getSeverity()); + if (log.getSeverity() >= consoleLevel) { + console::log(log.toString(), log.getSeverity()); + } } } diff --git a/loader/src/platform/windows/console.cpp b/loader/src/platform/windows/console.cpp index 59b76e3ac..773bf6ad9 100644 --- a/loader/src/platform/windows/console.cpp +++ b/loader/src/platform/windows/console.cpp @@ -32,8 +32,12 @@ void setupConsole(bool forceUseEscapeCodes = false) { } } + Severity consoleLevel = log::Logger::get()->getConsoleLogLevel(); + for (auto const& log : log::Logger::get()->list()) { - console::log(log.toString(), log.getSeverity()); + if (log.getSeverity() >= consoleLevel) { + console::log(log.toString(), log.getSeverity()); + } } }