From 3641b9891d7348e279f4e8853a9dadbfd491adfb Mon Sep 17 00:00:00 2001 From: yzct12345 <87620833+yzct12345@users.noreply.github.com> Date: Sat, 24 Jun 2023 02:58:49 +0300 Subject: [PATCH] logging: Simplify and make thread-safe This simplifies the logging system. This also fixes some lost messages on startup. The simplification is simple. I removed unused functions and moved most things in the .h to the .cpp. I replaced the unnecessary linked list with its contents laid out as three member variables. Anything that went through the linked list now directly accesses the backends. Generic functions are replaced with those for each specific use case and there aren't many. This change increases coupling but we gain back more KISS and encapsulation. With those changes it was easy to make it thread-safe. I just removed the mutex and turned a boolean atomic. I was planning to use this thread-safety in my next PR about stacktraces. It was actually async-signal-safety at first but I ended up using a different approach. Anyway getting rid of the linked list is important for that because have the list of backends constantly changing complicates things. --- src/citra/citra.cpp | 22 +- src/citra_qt/debugger/console.cpp | 11 +- src/citra_qt/main.cpp | 20 +- src/common/logging/backend.cpp | 381 ++++++++++++++++++----------- src/common/logging/backend.h | 130 +--------- src/common/logging/filter.cpp | 7 + src/common/logging/filter.h | 3 + src/core/core.cpp | 9 +- src/core/core.h | 9 +- src/dedicated_room/citra-room.cpp | 13 +- src/tests/common/param_package.cpp | 2 + 11 files changed, 282 insertions(+), 325 deletions(-) diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 7fc8188c8..aade3ee9c 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -174,25 +174,10 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) { std::cout << std::endl << "* " << message << std::endl << std::endl; } -static void InitializeLogging() { - using namespace Common; - - Log::Filter log_filter(Log::Level::Debug); - log_filter.ParseFilterString(Settings::values.log_filter.GetValue()); - Log::SetGlobalFilter(log_filter); - - Log::AddBackend(std::make_unique()); - - const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); - FileUtil::CreateFullPath(log_dir); - Log::AddBackend(std::make_unique(log_dir + LOG_FILE)); -#ifdef _WIN32 - Log::AddBackend(std::make_unique()); -#endif -} - /// Application entry point int main(int argc, char** argv) { + Common::Log::Initialize(); + Common::Log::SetColorConsoleBackendEnabled(true); Common::DetachedTasks detached_tasks; Config config; int option_index = 0; @@ -203,8 +188,6 @@ int main(int argc, char** argv) { std::string movie_play; std::string dump_video; - InitializeLogging(); - char* endarg; #ifdef _WIN32 int argc_w; @@ -346,6 +329,7 @@ int main(int argc, char** argv) { return -1; } + Core::System::InitializeGlobalInstance(); auto& system = Core::System::GetInstance(); auto& movie = Core::Movie::GetInstance(); diff --git a/src/citra_qt/debugger/console.cpp b/src/citra_qt/debugger/console.cpp index 4fb8d83db..8187b0b1d 100644 --- a/src/citra_qt/debugger/console.cpp +++ b/src/citra_qt/debugger/console.cpp @@ -21,6 +21,7 @@ void ToggleConsole() { console_shown = UISettings::values.show_console.GetValue(); } + using namespace Common::Log; #ifdef _WIN32 FILE* temp; if (UISettings::values.show_console) { @@ -29,24 +30,20 @@ void ToggleConsole() { freopen_s(&temp, "CONIN$", "r", stdin); freopen_s(&temp, "CONOUT$", "w", stdout); freopen_s(&temp, "CONOUT$", "w", stderr); - Common::Log::AddBackend(std::make_unique()); + SetColorConsoleBackendEnabled(true); } } else { if (FreeConsole()) { // In order to close the console, we have to also detach the streams on it. // Just redirect them to NUL if there is no console window - Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name()); + SetColorConsoleBackendEnabled(false); freopen_s(&temp, "NUL", "r", stdin); freopen_s(&temp, "NUL", "w", stdout); freopen_s(&temp, "NUL", "w", stderr); } } #else - if (UISettings::values.show_console) { - Log::AddBackend(std::make_unique()); - } else { - Log::RemoveBackend(Log::ColorConsoleBackend::Name()); - } + SetColorConsoleBackendEnabled(UISettings::values.show_console.GetValue()); #endif } } // namespace Debugger diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index b69bcf3d0..71f293785 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -146,21 +146,6 @@ void GMainWindow::ShowTelemetryCallout() { const int GMainWindow::max_recent_files_item; -static void InitializeLogging() { - using namespace Common; - - Log::Filter log_filter; - log_filter.ParseFilterString(Settings::values.log_filter.GetValue()); - Log::SetGlobalFilter(log_filter); - - const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); - FileUtil::CreateFullPath(log_dir); - Log::AddBackend(std::make_unique(log_dir + LOG_FILE)); -#ifdef _WIN32 - Log::AddBackend(std::make_unique()); -#endif -} - static QString PrettyProductName() { #ifdef _WIN32 // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2 @@ -186,7 +171,6 @@ static QString PrettyProductName() { GMainWindow::GMainWindow(Core::System& system_) : ui{std::make_unique()}, system{system_}, movie{Core::Movie::GetInstance()}, config{std::make_unique()}, emu_thread{nullptr} { - InitializeLogging(); Debugger::ToggleConsole(); Settings::LogSettings(); @@ -2869,6 +2853,7 @@ static Qt::HighDpiScaleFactorRoundingPolicy GetHighDpiRoundingPolicy() { } int main(int argc, char* argv[]) { + Common::Log::Initialize(); Common::DetachedTasks detached_tasks; MicroProfileOnThreadCreate("Frontend"); SCOPE_EXIT({ MicroProfileShutdown(); }); @@ -2893,7 +2878,8 @@ int main(int argc, char* argv[]) { // generating shaders setlocale(LC_ALL, "C"); - Core::System& system = Core::System::GetInstance(); + Core::System::InitializeGlobalInstance(); + auto& system{Core::System::GetInstance()}; GMainWindow main_window(system); // Register frontend applets diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 3c8f8f54d..60d87b1b8 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -2,13 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include #include -#include -#include -#include #include #include +#include "common/common_paths.h" #ifdef _WIN32 #include // For _SH_DENYWR #include // For OutputDebugStringW @@ -16,6 +13,9 @@ #define _SH_DENYWR 0 #endif #include "common/file_util.h" +#include "common/literals.h" +#include "common/settings.h" +#include "common/thread.h" #include "common/logging/backend.h" #include "common/logging/log.h" #include "common/logging/text_formatter.h" @@ -24,14 +24,176 @@ namespace Common::Log { +namespace { + +/** + * Interface for logging backends. + */ +class Backend { +public: + virtual ~Backend() = default; + + virtual void Write(const Entry& entry) = 0; + + virtual void EnableForStacktrace() = 0; + + virtual void Flush() = 0; +}; + +/** + * Backend that writes to stderr and with color + */ +class ColorConsoleBackend final : public Backend { +public: + explicit ColorConsoleBackend() = default; + + ~ColorConsoleBackend() override = default; + + void Write(const Entry& entry) override { + if (enabled.load(std::memory_order_relaxed)) { + PrintColoredMessage(entry); + } + } + + void Flush() override { + // stderr shouldn't be buffered + } + + void EnableForStacktrace() override { + enabled = true; + } + + void SetEnabled(bool enabled_) { + enabled = enabled_; + } + +private: + std::atomic_bool enabled{false}; +}; + +/** + * Backend that writes to a file passed into the constructor + */ +class FileBackend final : public Backend { +public: + explicit FileBackend(const std::string& filename) { + auto old_filename = filename; + old_filename += ".old.txt"; + + // Existence checks are done within the functions themselves. + // We don't particularly care if these succeed or not. + static_cast(FileUtil::Delete(old_filename)); + static_cast(FileUtil::Rename(filename, old_filename)); + + // _SH_DENYWR allows read only access to the file for other programs. + // It is #defined to 0 on other platforms + file = std::make_unique(filename, "w", _SH_DENYWR); + } + + ~FileBackend() override = default; + + void Write(const Entry& entry) override { + if (!enabled) { + return; + } + + bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); + + using namespace Common::Literals; + // Prevent logs from exceeding a set maximum size in the event that log entries are spammed. + const auto write_limit = 100_MiB; + const bool write_limit_exceeded = bytes_written > write_limit; + if (entry.log_level >= Level::Error || write_limit_exceeded) { + if (write_limit_exceeded) { + // Stop writing after the write limit is exceeded. + // Don't close the file so we can print a stacktrace if necessary + enabled = false; + } + file->Flush(); + } + } + + void Flush() override { + file->Flush(); + } + + void EnableForStacktrace() override { + enabled = true; + bytes_written = 0; + } + +private: + std::unique_ptr file; + bool enabled = true; + std::size_t bytes_written = 0; +}; + +/** + * Backend that writes to Visual Studio's output window + */ +class DebuggerBackend final : public Backend { +public: + explicit DebuggerBackend() = default; + + ~DebuggerBackend() override = default; + + void Write(const Entry& entry) override { +#ifdef _WIN32 + ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); +#endif + } + + void Flush() override {} + + void EnableForStacktrace() override {} +}; + +#ifdef ANDROID +/** + * Backend that writes to the Android logcat + */ +class LogcatBackend : public Backend { +public: + explicit LogcatBackend() = default; + + ~LogcatBackend() override = default; + + void Write(const Entry& entry) override { + PrintMessageToLogcat(entry); + } + + void Flush() override {} + + void EnableForStacktrace() override {} +}; +#endif + +bool initialization_in_progress_suppress_logging = false; + /** * Static state as a singleton. */ class Impl { public: static Impl& Instance() { - static Impl backend; - return backend; + if (!instance) { + abort(); + } + return *instance; + } + + static void Initialize() { + if (instance) { + abort(); + } + initialization_in_progress_suppress_logging = true; + const auto& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); + void(FileUtil::CreateDir(log_dir)); + Filter filter; + filter.ParseFilterString(Settings::values.log_filter.GetValue()); + instance = std::unique_ptr(new Impl(log_dir + LOG_FILE, filter), + Deleter); + initialization_in_progress_suppress_logging = false; } Impl(const Impl&) = delete; @@ -40,74 +202,54 @@ public: Impl(Impl&&) = delete; Impl& operator=(Impl&&) = delete; - void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, - const char* function, std::string message) { - message_queue.Push( - CreateEntry(log_class, log_level, filename, line_num, function, std::move(message))); - } - - void AddBackend(std::unique_ptr backend) { - std::lock_guard lock{writing_mutex}; - backends.push_back(std::move(backend)); - } - - void RemoveBackend(std::string_view backend_name) { - std::lock_guard lock{writing_mutex}; - - std::erase_if(backends, [&backend_name](const auto& backend) { - return backend_name == backend->GetName(); - }); - } - - const Filter& GetGlobalFilter() const { - return filter; - } - void SetGlobalFilter(const Filter& f) { filter = f; } - Backend* GetBackend(std::string_view backend_name) { - const auto it = - std::find_if(backends.begin(), backends.end(), - [&backend_name](const auto& i) { return backend_name == i->GetName(); }); - if (it == backends.end()) - return nullptr; - return it->get(); + void SetColorConsoleBackendEnabled(bool enabled) { + color_console_backend.SetEnabled(enabled); + } + + void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, + const char* function, std::string message) { + if (!filter.CheckMessage(log_class, log_level)) + return; + const Entry& entry = + CreateEntry(log_class, log_level, filename, line_num, function, std::move(message)); + message_queue.Push(entry); } private: - Impl() { - backend_thread = std::thread([&] { - Entry entry; - auto write_logs = [&](Entry& e) { - std::lock_guard lock{writing_mutex}; - for (const auto& backend : backends) { - backend->Write(e); - } - }; - while (true) { - entry = message_queue.PopWait(); - if (entry.final_entry) { - break; - } - write_logs(entry); - } - - // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a case - // where a system is repeatedly spamming logs even on close. - constexpr int MAX_LOGS_TO_WRITE = 100; - int logs_written = 0; - while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) { - write_logs(entry); - } - }); - } + Impl(const std::string& file_backend_filename, const Filter& filter_) + : filter{filter_}, file_backend{file_backend_filename}, backend_thread{std::thread([this] { + Common::SetCurrentThreadName("citra:Log"); + Entry entry; + const auto write_logs = [this, &entry]() { + ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); + }; + while (true) { + entry = message_queue.PopWait(); + if (entry.final_entry) { + break; + } + write_logs(); + } + // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a + // case where a system is repeatedly spamming logs even on close. + int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100; + while (max_logs_to_write-- && message_queue.Pop(entry)) { + write_logs(); + } + })} {} ~Impl() { - Entry entry; - entry.final_entry = true; - message_queue.Push(entry); + StopBackendThread(); + } + + void StopBackendThread() { + Entry stop_entry{}; + stop_entry.final_entry = true; + message_queue.Push(stop_entry); backend_thread.join(); } @@ -129,102 +271,51 @@ private: }; } - std::mutex writing_mutex; - std::thread backend_thread; - std::vector> backends; - Common::MPSCQueue message_queue; + void ForEachBackend(auto lambda) { + lambda(static_cast(debugger_backend)); + lambda(static_cast(color_console_backend)); + lambda(static_cast(file_backend)); + } + + static void Deleter(Impl* ptr) { + delete ptr; + } + + static inline std::unique_ptr instance{nullptr, Deleter}; + Filter filter; + DebuggerBackend debugger_backend{}; + ColorConsoleBackend color_console_backend{}; + FileBackend file_backend; + + std::thread backend_thread; + MPSCQueue message_queue{}; std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; }; +} // namespace -ConsoleBackend::~ConsoleBackend() = default; - -void ConsoleBackend::Write(const Entry& entry) { - PrintMessage(entry); +void Initialize() { + Impl::Initialize(); } -ColorConsoleBackend::~ColorConsoleBackend() = default; - -void ColorConsoleBackend::Write(const Entry& entry) { - PrintColoredMessage(entry); -} - -LogcatBackend::~LogcatBackend() = default; - -void LogcatBackend::Write(const Entry& entry) { - PrintMessageToLogcat(entry); -} - -FileBackend::FileBackend(const std::string& filename) { - const auto old_filename = filename + ".old.txt"; - - if (FileUtil::Exists(old_filename)) { - FileUtil::Delete(old_filename); - } - if (FileUtil::Exists(filename)) { - FileUtil::Rename(filename, old_filename); - } - - // _SH_DENYWR allows read only access to the file for other programs. - // It is #defined to 0 on other platforms - file = std::make_unique(filename, "w", _SH_DENYWR); -} - -FileBackend::~FileBackend() = default; - -void FileBackend::Write(const Entry& entry) { - if (!file->IsOpen()) { - return; - } - - // Prevent logs from exceeding a set maximum size in the event that log entries are spammed. - constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L; - - // Close the file after the write limit is exceeded. - if (bytes_written > MAX_BYTES_WRITTEN) { - file->Close(); - return; - } - - bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); - if (entry.log_level >= Level::Error) { - file->Flush(); - } -} - -DebuggerBackend::~DebuggerBackend() = default; - -void DebuggerBackend::Write(const Entry& entry) { -#ifdef _WIN32 - ::OutputDebugStringW(Common::UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); -#endif +void DisableLoggingInTests() { + initialization_in_progress_suppress_logging = true; } void SetGlobalFilter(const Filter& filter) { Impl::Instance().SetGlobalFilter(filter); } -void AddBackend(std::unique_ptr backend) { - Impl::Instance().AddBackend(std::move(backend)); -} - -void RemoveBackend(std::string_view backend_name) { - Impl::Instance().RemoveBackend(backend_name); -} - -Backend* GetBackend(std::string_view backend_name) { - return Impl::Instance().GetBackend(backend_name); +void SetColorConsoleBackendEnabled(bool enabled) { + Impl::Instance().SetColorConsoleBackendEnabled(enabled); } void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, const char* format, const fmt::format_args& args) { - auto& instance = Impl::Instance(); - const auto& filter = instance.GetGlobalFilter(); - if (!filter.CheckMessage(log_class, log_level)) - return; - - instance.PushEntry(log_class, log_level, filename, line_num, function, - fmt::vformat(format, args)); + if (!initialization_in_progress_suppress_logging) { + Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, + fmt::vformat(format, args)); + } } } // namespace Common::Log diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index 4d9117951..cb7839ee9 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h @@ -4,136 +4,22 @@ #pragma once -#include -#include -#include +#include #include "common/logging/filter.h" -#include "common/logging/log.h" - -namespace FileUtil { -class IOFile; -} namespace Common::Log { -/** - * Interface for logging backends. As loggers can be created and removed at runtime, this can be - * used by a frontend for adding a custom logging backend as needed - */ -class Backend { -public: - virtual ~Backend() = default; +class Filter; - virtual void SetFilter(const Filter& new_filter) { - filter = new_filter; - } - virtual const char* GetName() const = 0; - virtual void Write(const Entry& entry) = 0; +/// Initializes the logging system. This should be the first thing called in main. +void Initialize(); -private: - Filter filter; -}; +void DisableLoggingInTests(); /** - * Backend that writes to stderr without any color commands - */ -class ConsoleBackend : public Backend { -public: - ~ConsoleBackend() override; - - static const char* Name() { - return "console"; - } - const char* GetName() const override { - return Name(); - } - void Write(const Entry& entry) override; -}; - -/** - * Backend that writes to stderr and with color - */ -class ColorConsoleBackend : public Backend { -public: - ~ColorConsoleBackend() override; - - static const char* Name() { - return "color_console"; - } - - const char* GetName() const override { - return Name(); - } - void Write(const Entry& entry) override; -}; - -/** - * Backend that writes to the Android logcat - */ -class LogcatBackend : public Backend { -public: - ~LogcatBackend() override; - - static const char* Name() { - return "logcat"; - } - - const char* GetName() const override { - return Name(); - } - void Write(const Entry& entry) override; -}; - -/** - * Backend that writes to a file passed into the constructor - */ -class FileBackend : public Backend { -public: - ~FileBackend() override; - - explicit FileBackend(const std::string& filename); - - static const char* Name() { - return "file"; - } - - const char* GetName() const override { - return Name(); - } - - void Write(const Entry& entry) override; - -private: - std::unique_ptr file; - std::size_t bytes_written = 0; -}; - -/** - * Backend that writes to Visual Studio's output window - */ -class DebuggerBackend : public Backend { -public: - ~DebuggerBackend() override; - - static const char* Name() { - return "debugger"; - } - const char* GetName() const override { - return Name(); - } - void Write(const Entry& entry) override; -}; - -void AddBackend(std::unique_ptr backend); - -void RemoveBackend(std::string_view backend_name); - -Backend* GetBackend(std::string_view backend_name); - -/** - * The global filter will prevent any messages from even being processed if they are filtered. Each - * backend can have a filter, but if the level is lower than the global filter, the backend will - * never get the message + * The global filter will prevent any messages from even being processed if they are filtered. */ void SetGlobalFilter(const Filter& filter); + +void SetColorConsoleBackendEnabled(bool enabled); } // namespace Common::Log diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 0d33872c8..6213e3f49 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -204,4 +204,11 @@ bool Filter::CheckMessage(Class log_class, Level level) const { return static_cast(level) >= static_cast(class_levels[static_cast(log_class)]); } + +bool Filter::IsDebug() const { + return std::any_of(class_levels.begin(), class_levels.end(), [](const Level& l) { + return static_cast(l) <= static_cast(Level::Debug); + }); +} + } // namespace Common::Log diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h index 26d5f7392..681e5922b 100644 --- a/src/common/logging/filter.h +++ b/src/common/logging/filter.h @@ -56,6 +56,9 @@ public: /// Matches class/level combination against the filter, returning true if it passed. bool CheckMessage(Class log_class, Level level) const; + /// Returns true if any logging classes are set to debug + bool IsDebug() const; + private: std::array(Class::Count)> class_levels; }; diff --git a/src/core/core.cpp b/src/core/core.cpp index a4ad8a817..e9aff1a12 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -51,8 +51,6 @@ namespace Core { -/*static*/ System System::s_instance; - template <> Core::System& Global() { return System::GetInstance(); @@ -70,6 +68,13 @@ Core::Timing& Global() { System::~System() = default; +void System::InitializeGlobalInstance() { + if (s_instance) { + std::abort(); + } + s_instance = std::unique_ptr(new System); +} + System::ResultStatus System::RunLoop(bool tight_loop) { status = ResultStatus::Success; if (!IsPoweredOn()) { diff --git a/src/core/core.h b/src/core/core.h index b29fc20e2..b85febf26 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -74,9 +74,14 @@ public: * @returns Reference to the instance of the System singleton class. */ [[nodiscard]] static System& GetInstance() { - return s_instance; + if (!s_instance) { + std::abort(); + } + return *s_instance; } + static void InitializeGlobalInstance(); + /// Enumeration representing the return values of the System Initialize and Load process. enum class ResultStatus : u32 { Success, ///< Succeeded @@ -392,7 +397,7 @@ private: std::unique_ptr exclusive_monitor; private: - static System s_instance; + inline static std::unique_ptr s_instance; std::atomic_bool is_powered_on{}; diff --git a/src/dedicated_room/citra-room.cpp b/src/dedicated_room/citra-room.cpp index 4e68b3496..78fef0e90 100644 --- a/src/dedicated_room/citra-room.cpp +++ b/src/dedicated_room/citra-room.cpp @@ -150,17 +150,8 @@ static void SaveBanList(const Network::Room::BanList& ban_list, const std::strin } static void InitializeLogging(const std::string& log_file) { - using namespace Common; - - Log::AddBackend(std::make_unique()); - - const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); - FileUtil::CreateFullPath(log_dir); - Log::AddBackend(std::make_unique(log_dir + log_file)); - -#ifdef _WIN32 - Log::AddBackend(std::make_unique()); -#endif + Common::Log::Initialize(); + Common::Log::SetColorConsoleBackendEnabled(true); } /// Application entry point diff --git a/src/tests/common/param_package.cpp b/src/tests/common/param_package.cpp index 75442cea4..b2a349d50 100644 --- a/src/tests/common/param_package.cpp +++ b/src/tests/common/param_package.cpp @@ -4,11 +4,13 @@ #include #include +#include "common/logging/backend.h" #include "common/param_package.h" namespace Common { TEST_CASE("ParamPackage", "[common]") { + Common::Log::DisableLoggingInTests(); ParamPackage original{ {"abc", "xyz"}, {"def", "42"},