diff --git a/documents/Debugging/Debugging.md b/documents/Debugging/Debugging.md index 8bb4b8fbd..013ca15fb 100644 --- a/documents/Debugging/Debugging.md +++ b/documents/Debugging/Debugging.md @@ -73,6 +73,8 @@ You can configure the emulator by editing the `config.toml` file found in the `u - Examples: - If the log is being spammed with messages coming from Lib.Pad, you can use `Lib.Pad:Critical` to only log critical-level messages. - If you'd like to mute everything, but still want to receive messages from Vulkan rendering: `*:Critical Render.Vulkan:Info` + - `isIdenticalLogGrouped`: Group same logs in one line with a counter (`true`/`false`) + - By default, the emulator will not rewrite the same line, and instead add a counter. - `Fullscreen`: Display the game in a full screen borderless window. diff --git a/src/common/config.cpp b/src/common/config.cpp index a5eea0a64..fb1181d62 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -141,6 +141,7 @@ static ConfigEntry isTrophyPopupDisabled(false); static ConfigEntry trophyNotificationDuration(6.0); static ConfigEntry logFilter(""); static ConfigEntry logType("sync"); +static ConfigEntry isIdenticalLogGrouped(true); static ConfigEntry userName("shadPS4"); static ConfigEntry isShowSplash(false); static ConfigEntry isSideTrophy("right"); @@ -395,6 +396,10 @@ string getLogType() { return logType.get(); } +bool groupIdenticalLogs() { + return isIdenticalLogGrouped.get(); +} + string getUserName() { return userName.get(); } @@ -694,6 +699,10 @@ void setLogType(const string& type, bool is_game_specific) { logType.set(type, is_game_specific); } +void setIdenticalLogGrouped(bool enable, bool is_game_specific) { + isIdenticalLogGrouped.set(enable, is_game_specific); +} + void setLogFilter(const string& type, bool is_game_specific) { logFilter.set(type, is_game_specific); } @@ -893,6 +902,7 @@ void load(const std::filesystem::path& path, bool is_game_specific) { enableDiscordRPC = toml::find_or(general, "enableDiscordRPC", enableDiscordRPC); logFilter.setFromToml(general, "logFilter", is_game_specific); logType.setFromToml(general, "logType", is_game_specific); + isIdenticalLogGrouped.setFromToml(general, "isIdenticalLogGrouped", is_game_specific); userName.setFromToml(general, "userName", is_game_specific); isShowSplash.setFromToml(general, "showSplash", is_game_specific); isSideTrophy.setFromToml(general, "sideTrophy", is_game_specific); @@ -1081,6 +1091,7 @@ void save(const std::filesystem::path& path, bool is_game_specific) { is_game_specific); logFilter.setTomlValue(data, "General", "logFilter", is_game_specific); logType.setTomlValue(data, "General", "logType", is_game_specific); + isIdenticalLogGrouped.setTomlValue(data, "General", "isIdenticalLogGrouped", is_game_specific); userName.setTomlValue(data, "General", "userName", is_game_specific); isShowSplash.setTomlValue(data, "General", "showSplash", is_game_specific); isSideTrophy.setTomlValue(data, "General", "sideTrophy", is_game_specific); @@ -1224,6 +1235,7 @@ void setDefaultValues(bool is_game_specific) { trophyNotificationDuration.set(6.0, is_game_specific); logFilter.set("", is_game_specific); logType.set("sync", is_game_specific); + isIdenticalLogGrouped.set("isIdenticalLogGrouped", is_game_specific); userName.set("shadPS4", is_game_specific); isShowSplash.set(false, is_game_specific); isSideTrophy.set("right", is_game_specific); diff --git a/src/common/config.h b/src/common/config.h index a9e0f7010..eb2b91f52 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -101,6 +101,8 @@ void setPipelineCacheEnabled(bool enable, bool is_game_specific = false); void setPipelineCacheArchived(bool enable, bool is_game_specific = false); std::string getLogType(); void setLogType(const std::string& type, bool is_game_specific = false); +bool groupIdenticalLogs(); +void setGroupIdenticalLogs(bool enable, bool is_game_specific = false); std::string getLogFilter(); void setLogFilter(const std::string& type, bool is_game_specific = false); double getTrophyNotificationDuration(); diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 168350b96..9b7ea9cd1 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -209,41 +209,62 @@ public: } } - std::unique_lock entry_loc(_mutex); - - if (_last_entry.message == message) { - ++_last_entry.counter; - return; - } - - if (_last_entry.counter >= 2) { - _last_entry.message += " x" + std::to_string(_last_entry.counter); - } - - if (_last_entry.counter >= 1) { - if (Config::getLogType() == "async") { - message_queue.EmplaceWait(_last_entry); - } else { - ForEachBackend([this](auto& backend) { backend.Write(this->_last_entry); }); - std::fflush(stdout); - } - } - using std::chrono::duration_cast; using std::chrono::microseconds; using std::chrono::steady_clock; - this->_last_entry = { - .timestamp = duration_cast(steady_clock::now() - time_origin), - .log_class = log_class, - .log_level = log_level, - .filename = filename, - .line_num = line_num, - .function = function, - .message = message, - .thread = Common::GetCurrentThreadName(), - .counter = 1, - }; + if (Config::groupIdenticalLogs()) { + std::unique_lock entry_loc(_mutex); + + if (_last_entry.message == message) { + ++_last_entry.counter; + return; + } + + if (_last_entry.counter >= 2) { + _last_entry.message += " x" + std::to_string(_last_entry.counter); + } + + if (_last_entry.counter >= 1) { + if (Config::getLogType() == "async") { + message_queue.EmplaceWait(_last_entry); + } else { + ForEachBackend([this](auto& backend) { backend.Write(this->_last_entry); }); + std::fflush(stdout); + } + } + + this->_last_entry = { + .timestamp = duration_cast(steady_clock::now() - time_origin), + .log_class = log_class, + .log_level = log_level, + .filename = filename, + .line_num = line_num, + .function = function, + .message = message, + .thread = Common::GetCurrentThreadName(), + .counter = 1, + }; + } else { + const Entry entry = { + .timestamp = duration_cast(steady_clock::now() - time_origin), + .log_class = log_class, + .log_level = log_level, + .filename = filename, + .line_num = line_num, + .function = function, + .message = message, + .thread = Common::GetCurrentThreadName(), + .counter = 1, + }; + + if (Config::getLogType() == "async") { + message_queue.EmplaceWait(entry); + } else { + ForEachBackend([&entry](auto& backend) { backend.Write(entry); }); + std::fflush(stdout); + } + } } private: @@ -275,21 +296,23 @@ private: } void StopBackendThread() { - // log last message - if (_last_entry.counter >= 2) { - _last_entry.message += " x" + std::to_string(_last_entry.counter); - } - - if (_last_entry.counter >= 1) { - if (Config::getLogType() == "async") { - message_queue.EmplaceWait(_last_entry); - } else { - ForEachBackend([this](auto& backend) { backend.Write(this->_last_entry); }); - std::fflush(stdout); + if (Config::groupIdenticalLogs()) { + // log last message + if (_last_entry.counter >= 2) { + _last_entry.message += " x" + std::to_string(_last_entry.counter); } - } - this->_last_entry = {}; + if (_last_entry.counter >= 1) { + if (Config::getLogType() == "async") { + message_queue.EmplaceWait(_last_entry); + } else { + ForEachBackend([this](auto& backend) { backend.Write(this->_last_entry); }); + std::fflush(stdout); + } + } + + this->_last_entry = {}; + } backend_thread.request_stop(); if (backend_thread.joinable()) { diff --git a/src/emulator.cpp b/src/emulator.cpp index 87ce82326..5d3a652b8 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -239,6 +239,7 @@ void Emulator::Run(std::filesystem::path file, std::vector args, LOG_INFO(Config, "Game-specific config exists: {}", has_game_config); LOG_INFO(Config, "General LogType: {}", Config::getLogType()); + LOG_INFO(Config, "General isIdenticalLogGrouped: {}", Config::groupIdenticalLogs()); LOG_INFO(Config, "General isNeo: {}", Config::isNeoModeConsole()); LOG_INFO(Config, "General isDevKit: {}", Config::isDevKitConsole()); LOG_INFO(Config, "General isConnectedToNetwork: {}", Config::getIsConnectedToNetwork());